home *** CD-ROM | disk | FTP | other *** search
Wrap
#line 1 "/fzi/prost/stone/SOS3-2/src/agg/Mapping.c" /* -------------------------------------------------------------------------- * Copyright 1992 by Forschungszentrum Informatik (FZI) * * You can use and distribute this software under the terms of the licence * you should have received along with this program. * If not or if you want additional information, write to * Forschungszentrum Informatik, "STONE", Haid-und-Neu-Strasse 10-14, * D-7500 Karlsruhe 1, Germany. * -------------------------------------------------------------------------- */ // ************************************************************************** // Module Mapping 9/8/90 Jochen Alt (ja) // modified : 24/10/91 (bs) // modified : 22/11/91 (ja) // ************************************************************************** // implements methods of classes: Mapping, sos_Map_node // ************************************************************************** #include "agg_err.h" #include "trc_agg.h" #include "sys.h" #include "Mapping.h" #include "agg_sos.h" // erweiterbares Hash-Verfahren mit leichten Modifikationen // Quelle : Datenbankhandbuch Seite 247 // siehe externe Dokumentation #define get_key_based_on_equal() get_role1_based_on_equal() #define get_info_based_on_equal() get_role2_based_on_equal() #define set_key_based_on_equal(b) set_role1_based_on_equal(b) #define set_info_based_on_equal(b) set_role2_based_on_equal(b) inline int absolute(int d) { return (d>0)?d:-d; } const sos_Offset NIL=0; // Da psm direkt benutzt wird, wird ein Datentyp benoetigt, der // anstelle der Objekte abgespeichert wird. Dies ist ein object_save_t, // der augenblicklich als sos_Typed_id definiert ist. Die beiden Operatoren, // die object_save_t in sos_Object und umgekehrt konvertieren, sind // make_object_save und make_Object // ************************************************************************** object_save_t make_object_save (sos_Object o) // ************************************************************************** // nimm das sos_Object o, bastle daraus ein object_save, und // liefere es in os zurueck { sos_Typed_id tpid = o.typed_id(); object_save_t os; bcopy_from_sos_Typed_id(&tpid,&os); return os; } // ************************************************************************** sos_Object make_Object (object_save_t &os) // ************************************************************************** // nimm den object_save os, mache daraus ein sos_Object und liefere es zurueck { sos_Typed_id tpid; bcopy_to_sos_Typed_id(&tpid,&os); return sos_Object::make(tpid); } /* Abhaengig von sos_Bool:list_cursor gibt es zwei Versionen eines Mappings: 1. Version: list_cursor = FALSE Die Cursoroperatoren sind nur auf einem statischen Mapping definiert. Wird ein Objekt eingefuegt, so kan sich die Cursor-Reihenfolge voellig veraendern. Damit ist ein sinnvoller Umgang mit Cursorn nur auf einem sich nicht veraendernden Mapping moeglich. Sobald etwas eingefuegt oder geloescht wird, sind die Cursor zu schliessen und neu von vorne zu beginnen. Die Reihenfolge der Objekte ist durch die Reihenfolge in der Hashtabelle definiert, also fuer den Benutzer rein zufaellig. In dieser Version braucht ein Eintrag 36 Byte (= 2x sos_Object und ein Hashwert a 4 byte) 2. Version : list_cursor = TRUE Die Cursor-Operatoren koennen auch an einem dynamischen Mapping gleich- zeitig ausgefuehrt werden. Die einzelnen Eintraege sind miteinander doppelt verkettet, ein Einfuegen erfolgt am Anfang der Liste. Ein Eintrag braucht damit 36+2x16 = 68 Byte (zusaetzlich ein Vorwaerts und ein Rueckwaertszeiger). Damit werden die Prozeduren remove_at, insert_before und insert_after sinnvoll. In der Implementierung wird auf einer Seite die struct-Komponente list, bestehend aus den beiden Verweisen zusaetzlich abgespeichert. In Funktionen, die ein Objekt zurueckliefern sollen, jedoch keines zum Liefern haben, wird my_no_object als Ruckgabeparameter von benutzt. Weiterhin dient my_no_object als Ende-der-Liste Marke im ersten und letzten Element des Mappings. Kurzum, my_no_object ist ein Mapping- lokales NO_OBJECT, und dient dazu, das Einfuegen von NO_OBJECT zu ermoeglichen. Stattdessen kann man eben my_no_object nicht einfuegen. Faktisch wird my_no_object immer mit dem augenblicklich benutzten Mapping initialisiert, so dass ein Mapping nicht in sich selbst eingefuegt werden kann. Dieses wird entsprechend abgefangen. */ #define PAGE_SIZE(list_cursor) \ (list_cursor?page_size_with_list: page_size_without_list) // max_page_entries = die maximale Anzahl an Eintraegen auf einer Seite #define MAX_PAGE_ENTRIES(list_cursor) \ (list_cursor? max_page_entries_with_list: max_page_entries_without_list) // aus einer gegebenen Position in der Hashtabelle und der lokalen Tiefe // der zugehoerigen Seitenliste kann die erste Position in der Hashtabelle // berechnet werden, die auf diesselbe Seitenliste zeigt. #define FIRST_LINK(ht_pos,local_depth) \ ((LSHIFT(1,local_depth)-1) BITAND ht_pos) /* *********** Implementierung von sos_Map_node ********************* Ein sos_Map_node enthaelt nur die Komponente sos_Object:key_val, die dasjenige Objekt angibt, auf das der Cursor augenblicklich zeigt. Ein sos_Map_node ist eindeutig einem Cursor zugeordnet und liegt in dessen Container. Ist der Cursor ungueltig, so ist die Komponente Cursor->get_key_val() == NO_OBJECT, andernfalls zeigt sie auf einen sos_Map_node. Also wird beim Starten auf einem Mapping mit Inhalt ein sos_Map_node erzeugt und dem Cursor zugeordnet, beim Verlassen des letzten gueltigen Elements wird der sos_Map_node wieder geloescht. */ // ************************************************************************** sos_Int hash_id (sos_Object o) // ************************************************************************** // benutzt die Adresse des Objektes, um daraus eine Zahl zu basteln // Offsets liegen nur auf durch 8 teilbaren Addressen // => die ersten 3 Bit sind nutzlos, wird sie weg. // Ist das Objekt aber keine Klassen-Instanz, sondern // ein externes Objekt (z.B. sos_Int,sos_Char), // so wird der Offset benutzt, indem der Wert steht. { sos_Int h = o.offset(); if (o.container() != UNUSED_CONTAINER) h = RSHIFT(h,3) BITXOR sos_Int(o.container()); return h; } // ** hash_id ** // ************************************************************************** ht_entry_t read_ht_entry(sos_Container ct, sos_Offset ht_offset, sos_Int ht_pos) // ************************************************************************** { union {sos_Offset dummy; char mem [HT_ENTRY_SIZE];} u; ct.read(ht_offset+ht_pos*HT_ENTRY_SIZE, HT_ENTRY_SIZE, &u); ht_entry_t ht_entry; bcopy_to_sos_Offset(&ht_entry.page_list_offset,&u.mem[0]); bcopy_to_sos_Char(&ht_entry.local_depth, &u.mem[SOS_OFFSET_SIZE] ); return ht_entry; } // ** read_ht_entry ** // ************************************************************************** void write_ht_entry(sos_Container ct, sos_Offset ht_offset, sos_Int ht_pos, ht_entry_t ht_entry) // ************************************************************************** { union {sos_Offset dummy; char mem [HT_ENTRY_SIZE];} u; bcopy_from_sos_Offset(&ht_entry.page_list_offset, &u.mem[0]); bcopy_from_sos_Char(&ht_entry.local_depth, &u.mem[SOS_OFFSET_SIZE]); ct.write(ht_offset+ht_pos*HT_ENTRY_SIZE, HT_ENTRY_SIZE, &u); } // ** write_ht_entry ** // ************************************************************************** page_header_t read_page_header(sos_Container ct, sos_Offset page_offset) // ************************************************************************** { union {sos_Offset dummy; char mem [PAGE_HEADER_SIZE];} u; page_header_t page_header; ct.read (page_offset, PAGE_HEADER_SIZE, &u); bcopy_to_sos_Offset(&page_header.next_page, &u.mem[0]); bcopy_to_sos_Char(&page_header.pages, &u.mem[SOS_OFFSET_SIZE]); bcopy_to_sos_Char(&page_header.entries_on_last_page, &u.mem[SOS_OFFSET_SIZE+CHAR_SIZE]); return page_header; } // ** read_page_header ** // ************************************************************************** void write_page_header(sos_Container ct, sos_Offset page_offset, page_header_t& page_header) // ************************************************************************** { union {sos_Offset dummy; char mem [PAGE_HEADER_SIZE];} u; bcopy_from_sos_Offset(&page_header.next_page,&u.mem[0]); bcopy_from_sos_Char(&page_header.pages,&u.mem[SOS_OFFSET_SIZE]); bcopy_from_sos_Char(&page_header.entries_on_last_page, &u.mem[SOS_OFFSET_SIZE+CHAR_SIZE]); ct.write (page_offset, PAGE_HEADER_SIZE, &u); } // ** write_page_header ** /* Eine Seitenliste besteht aus einzelnen Seiten des Typs page_t, wobei diese durch Vorwaertszeiger verkettet sind. Auf jeder Seite befindet sich ein Seitenkopf mit den Angaben: sos_Char pages; => Anzahl der Seiten in der Seitenliste sos_Char entries_on_last_page; => Eintraege auf der letzten Seite, alle anderen Seite sind voll, d.h. sie haben max_page_entries Eintraege. Diese Angabe wird nur auf der ersten Seite verwaltet, auf den restlichen Seiten ist entries_on_last_page undefiniert. sos_Offset next_page; => Verweis auf die naechste Seite, bei der letzten Seite undefiniert. */ // ************************************************************************** void destroy_page_list(sos_Bool list_cursor, sos_Container ct, sos_Offset page_offset, sos_Int no_of_pages) // ************************************************************************** // deallokiere im Container ct die Seitenliste bestehend aus den // Typen page_t, wobei die erste zu deallokierende Seite auf page_offset // liegt. Insgesamt sind no_of_pages Seiten zu loeschen. { sos_Offset act_page_offset = page_offset; sos_Offset next_page_offset; for (sos_Int i=1;i<=no_of_pages;i++) { // Lese zuerst naechsten Verweis, bevor die Seite deallokiert wird page_header_t page_header = read_page_header(ct, act_page_offset); next_page_offset = page_header.next_page; ct.deallocate(act_page_offset,PAGE_SIZE(list_cursor)); act_page_offset = next_page_offset; } } // ** destroy_page_list ** // ************************************************************************** void read_page( sos_Bool list_cursor, sos_Container ct, sos_Offset page_offset, sos_Int no_of_entries, page_t& page) // ************************************************************************** // Lese Seite ab sos_Offset page_offset. Es werden no_of_entries // Eintraege eingelesen. { err_assert((no_of_entries>=0 AND no_of_entries<=MAX_PAGE_ENTRIES(list_cursor)), "Mapping:read_page_entry::internal Error"); page.page_header = read_page_header(ct,page_offset); union {sos_Typed_id dummy; char mem [MAX_PAGE_SIZE];} u; ct.read(page_offset+PAGE_HEADER_SIZE, no_of_entries*ENTRY_SIZE,&u); char *ptr = u.mem; for (int i = 0;i<no_of_entries;i++) { bcopy_to_sos_Typed_id(&page.entry[i].key,ptr); ptr += SOS_TYPED_ID_SIZE; bcopy_to_sos_Typed_id(&page.entry[i].info,ptr); ptr += SOS_TYPED_ID_SIZE; bcopy_to_sos_Int(&page.entry[i].hash_value,ptr); ptr += INT_SIZE; } if (list_cursor) { // Lese die Vorgaenger und Nachfolger Verweise ptr = u.mem; ct.read(page_offset+add_list_pos, no_of_entries*LIST_SIZE, &u); for (int i = 0; i < no_of_entries; i++) { bcopy_to_sos_Typed_id(&page.list[i].pred,ptr); ptr += SOS_TYPED_ID_SIZE; bcopy_to_sos_Typed_id(&page.list[i].succ,ptr); ptr += SOS_TYPED_ID_SIZE; } } } // ** read_page ** // ************************************************************************** sos_Int read_page( sos_Bool list_cursor, sos_Container ct, page_header_t first_page_header, ht_entry_t ht_entry, sos_Int page_no, page_t& page) // ************************************************************************** // Lese die page_no.te Seite, auf die der Hashtabelleneintrag ht_entry // zeigt, wobei der Seitenkopf der ersten Seite schon unter // first_page_header bekannt ist. { sos_Offset page_offset = ht_entry.page_list_offset; page.page_header = first_page_header; // Durchlaufe Seitenliste bis zur richtigen Seite for (sos_Int no = 1; no < page_no; no++) { if (no >1) page.page_header = read_page_header(ct,page_offset); page_offset =page.page_header.next_page; } sos_Int entries = (page_no < first_page_header.pages)? MAX_PAGE_ENTRIES(list_cursor): first_page_header.entries_on_last_page; read_page(list_cursor, ct, page_offset, entries, page); return entries; } // ** read_page ** // ************************************************************************** void read_page_entry(sos_Bool list_cursor, sos_Container ct, sos_Int page_offset, sos_Int entry_no, page_t& page) // ************************************************************************** // Lese auf der Seite auf page_offset den entry_no.ten Eintrag in page { err_assert((entry_no>=0 AND entry_no<=MAX_PAGE_ENTRIES(list_cursor)), "Mapping::read_page_entry::internal Error"); union {sos_Typed_id dummy; char mem[ENTRY_SIZE];} u; // note: ENTY_SIZE > LIST_SIZE ct.read( page_offset+PAGE_HEADER_SIZE+entry_no*ENTRY_SIZE, ENTRY_SIZE,&u); bcopy_to_sos_Typed_id(&page.entry[entry_no].key,&u.mem[0]); bcopy_to_sos_Typed_id(&page.entry[entry_no].info,&u.mem[SOS_TYPED_ID_SIZE]); bcopy_to_sos_Int(&page.entry[entry_no].hash_value, &u.mem[SOS_TYPED_ID_SIZE+SOS_TYPED_ID_SIZE]); if (list_cursor) { ct.read( page_offset+add_list_pos+entry_no*LIST_SIZE, LIST_SIZE,&u); bcopy_to_sos_Typed_id(&page.list[entry_no].pred,&u.mem[0]); bcopy_to_sos_Typed_id(&page.list[entry_no].succ, &u.mem[SOS_TYPED_ID_SIZE]); } } // ** read_page_entry ** // ************************************************************************** void write_page(sos_Bool list_cursor, sos_Container ct, sos_Offset page_offset, sos_Int no_of_entries, page_t& page) // ************************************************************************** // Schreibe die Seite page mit no_of_entries Eintraegen // an die Stelle page_offset { write_page_header(ct,page_offset,page.page_header); err_assert((no_of_entries>=0 AND no_of_entries<=MAX_PAGE_ENTRIES(list_cursor)), "Mapping::write_page::internal Error"); union {sos_Typed_id dummy; char mem [MAX_PAGE_SIZE];} u; char *ptr = u.mem; for (int i = 0; i < no_of_entries; i++) { bcopy_from_sos_Typed_id(&page.entry[i].key,ptr); ptr += SOS_TYPED_ID_SIZE; bcopy_from_sos_Typed_id(&page.entry[i].info,ptr); ptr += SOS_TYPED_ID_SIZE; bcopy_from_sos_Int(&page.entry[i].hash_value,ptr); ptr += INT_SIZE; } ct.write(page_offset+PAGE_HEADER_SIZE, no_of_entries*ENTRY_SIZE,&u); if (list_cursor) { ptr = u.mem; for (int i = 0; i < no_of_entries; i++) { bcopy_from_sos_Typed_id(&page.list[i].pred,ptr); ptr += SOS_TYPED_ID_SIZE; bcopy_from_sos_Typed_id(&page.list[i].succ,ptr); ptr += SOS_TYPED_ID_SIZE; } ct.write(page_offset+add_list_pos, LIST_SIZE*no_of_entries, &u); } } // ** write_page ** // ************************************************************************** void write_page_entry(sos_Bool list_cursor, sos_Container ct, sos_Int page_offset, sos_Int entry_no, page_t& page) // ************************************************************************** // Schreibe auf der Seite page den einzelnen Eintrag Nr entry_no // auf die Platte, der Seitenanfang beginnt bei page_offset. { err_assert((entry_no>=0 AND entry_no<=MAX_PAGE_ENTRIES(list_cursor)), "Mapping::write_page::internal Error"); union {sos_Typed_id dummy; char mem[ENTRY_SIZE];} u; // note: ENTY_SIZE > LIST_SIZE bcopy_from_sos_Typed_id(&page.entry[entry_no].key,&u.mem[0]); bcopy_from_sos_Typed_id(&page.entry[entry_no].info, &u.mem[SOS_TYPED_ID_SIZE]); bcopy_from_sos_Int(&page.entry[entry_no].hash_value, &u.mem[SOS_TYPED_ID_SIZE+SOS_TYPED_ID_SIZE]); ct.write( page_offset+PAGE_HEADER_SIZE+entry_no*ENTRY_SIZE, ENTRY_SIZE,&u); if (list_cursor) { bcopy_from_sos_Typed_id(&page.list[entry_no].pred,&u.mem[0]); bcopy_from_sos_Typed_id(&page.list[entry_no].succ, &u.mem[SOS_TYPED_ID_SIZE]); ct.write( page_offset+add_list_pos+entry_no*LIST_SIZE, LIST_SIZE,&u); } } // ** write_page_entry ** // search_page_for_key__F8sos_BoolR6page_tiiG10sos_Object // ************************************************************************** sos_Int search_page_for_key(sos_Bool key_based_on_equal, page_t& page, sos_Int entries, sos_Int org_hash_value, sos_Object key) // ************************************************************************** // durchsucht die Seite page mit entries Eintraegen nach dem sos_Object key, // das den Hashwert org_hash_value besitzt. Wird es gefunden, wird seine // Position in der Seite geliefert, ansonsten -1. { T_PROC("search_page_for_key"); TT(agg_M, T_ENTER); sos_Bool found = FALSE; for (sos_Int i=0; ((i<entries) AND (NOT found)); i++) { if (page.entry[i].hash_value == org_hash_value) { sos_Object o = make_Object(page.entry[i].key); if (agg_same_entity(o, key, key_based_on_equal, EQ_STRONG)) { found = TRUE; TT(agg_M, T_LEAVE); return i; } } } TT(agg_M, T_LEAVE); return -1; } // ** search_page_for_key ** // ************************************************************************** sos_Offset new_page_list(sos_Container ct, sos_Bool list_cursor) // ************************************************************************** // allokiere eine neue Seitenliste und liefere deren sos_Offset zurueck { T_PROC("new_page_list"); TT(agg_L, T_ENTER); sos_Offset new_page_offset = ct.allocate(PAGE_SIZE(list_cursor)); page_header_t page_header; page_header.entries_on_last_page = 0; page_header.pages = 1; write_page_header(ct,new_page_offset, page_header); TT(agg_L, T_LEAVE); return new_page_offset; } // ** new_page_list ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::write_succ_pred( sos_Typed_id &_tpid,sos_Container ct, sos_Bool key_based_on_equal, sos_Bool list_cursor, sos_Object key, sos_Bool write_succ, sos_Object succ_pred) // ************************************************************************** // Schreibe in den Vorgaeger bzw. Nachfolger von key succ_pred. // Ob Vor oder Nachgaenger ausgewaehlt wird, wird durch write_succ // geregelt. write_succ == TRUE schreibt succ_pred in den // Nachfolgerzeiger von key, ansonsten in den Vorgaengerzeiger von key. { T_PROC("Mapping::write_succ_pred"); TT(agg_M, T_ENTER); sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); if (my_no_object == key) { TT(agg_M, T_LEAVE); return; } sos_Int org_hash_value = absolute((key_based_on_equal)? key.hash_value():hash_id(key)); sos_Int ht_pos = org_hash_value MOD sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(); ht_entry_t ht_entry = read_ht_entry(ct,sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(),ht_pos); sos_Offset page_offset = ht_entry.page_list_offset; page_header_t first_page_header = read_page_header(ct, page_offset); sos_Int pages = first_page_header.pages; sos_Int entries_on_last_page = first_page_header.entries_on_last_page; sos_Int pos = -1; // entspricht "nicht gefunden" sos_Int entries_on_page = MAX_PAGE_ENTRIES(list_cursor); page_t page; for (sos_Int page_no = 1; (page_no<=pages) AND (pos == -1); page_no++) { if (page_no == pages) entries_on_page = entries_on_last_page; read_page(TRUE, ct, page_offset, entries_on_page, page); sos_Int pos = search_page_for_key ( key_based_on_equal,page, entries_on_page, org_hash_value, key ); if (pos == -1) page_offset = page.page_header.next_page; else { // Objekt gefunden if (write_succ) page.list[pos].succ = make_object_save(succ_pred); else page.list[pos].pred = make_object_save(succ_pred); write_page_entry(TRUE, ct,page_offset,pos, page); } } TT(agg_M, T_LEAVE); } // ** Mapping::write_succ_pred ** // ************************************************************************** sos_Offset increase_page_list( sos_Bool list_cursor, sos_Container ct, sos_Offset first_page_offset, page_header_t& first_page_header, sos_Offset last_page_offset) // ************************************************************************** // Eine Seitenliste, die an der Stelle ht_pos an der Hashtabelle // angehaengt ist, wird um eine Seite erweitert. diese Seite wird // hinten angehaengt. Ein Verweis auf die bisher letzte Seite { T_PROC("increase_page_list"); TT(agg_M, T_ENTER); sos_Offset new_page_offset = ct.allocate(PAGE_SIZE(list_cursor)); // Eigentlich sollte der Fall der Seitenlisten gaenzlich vermieden // werden. Wird nun eine Seitenliste tatsaechlich ueber 126 Seiten // gross, so ist entweder die Statistik fehlerhaft, oder es // sind tatsaechlich mehr als 3225 Eintraege mit demselben Hashwert im // Mapping. Diese Faelle fuehren zum Absturz, da die Variable, die die // Seiten in einer Seitenliste zaehlt, ein char ist. Ansich ist eine // solche Konstellation jedoch nicht vorstellbar. if (first_page_header.pages >= 126) err_raise(err_SYS, "statistical Error","Mapping:insert"); first_page_header.pages++; first_page_header.entries_on_last_page = 0; // Existierte bisher nur eine Seite ? if (first_page_header.pages == 2) first_page_header.next_page = new_page_offset; else { // Es gab schon mehr als eine Seite, der erste Seitenheader und // der Header der bisher letzten Seite muss geschrieben werden page_header_t last_page_header; last_page_header.next_page = new_page_offset; write_page_header(ct,last_page_offset, last_page_header); } write_page_header(ct,first_page_offset, first_page_header); TT(agg_M, T_LEAVE); return new_page_offset; } // ** Mapping::increase_page_list /* Um zu entscheiden, ob die Hashtabelle geschrumpft werden kann, muss bekannt sein, ob es Seitenlisten gibt, auf die nur ein HT-Eintrag verweist. Falls ja, ist ein Schrumpfen nicht moeglich. Da beim Teilen und Splitten einer Seite die lokale Tiefe veraendert wird, muss fuer jede moegliche lokale Tiefe die Anzahl der Verweise gespeichert werden, da beim Splitten 2 Seiten mit hoeheren lokalen Tiefen dazukommen und eine mit einer niedrigeren weg- faellt. Es gibt also einen von Hand allokierten Platz, auf dessen Anfang get_no_of_pages_offset() zeigt. In den ersten 4 Byte davon steht die Anzahl der Seitenlisten mit lokaler Tiefe 0 , in den naechsten 4 Byte die Anzahl mit lokaler Tiefe 1 etc. bis MAX_GLOBAL_DEPTH. */ // ************************************************************************** sos_Bool no_single_referenced_pages(sos_Container ct, sos_Offset no_of_pages_offset, sos_Int global_depth) // ************************************************************************** // liefert TRUE, falls es keine Seitenliste gibt, die von der Hashtabelle // nur von einem einzigen Verweis referenziert wird. Dies wird anhand // der Zaehler festgestellt, die fuer jede lokale Tiefe zaehlen, wieviel // Seitenlisten es mit dieser lokalen Tiefe gibt. Die Seitenlisten, die // nur von einem Verweis referenziert werden, zeichnen sich durch // lokale Tiefe == globale Tiefe aus. { sos_Int entry; union {sos_Int dummy; char mem [INT_SIZE];} u; // lese die Anzahl der Verweise auf Seitenlisten der lokalen // Tiefe global_depth ct.read(no_of_pages_offset+INT_SIZE*global_depth,INT_SIZE,&u); bcopy_to_sos_Int(&entry,&u); return sos_Bool (entry == 0); } // ** Mapping::no_single_referenced_pages ** // ************************************************************************** void add_no_of_pages(sos_Container ct, sos_Offset no_of_pages_offset, sos_Int local_depth, sos_Int value) // ************************************************************************** // zaehle zu der Anzahl von Verweisen auf eine Seitenliste mit der // lokalen Tiefe local_depth value dazu. { sos_Int entry; union {sos_Int dummy; char mem [INT_SIZE];} u; sos_Int offset = no_of_pages_offset+INT_SIZE*local_depth; ct.read(offset, INT_SIZE, &u); bcopy_to_sos_Int(&entry,&u); entry += value; bcopy_from_sos_Int(&entry,&u); ct.write(offset, INT_SIZE, &u); } // ** add_no_of_page ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::insert_in_page_list ( sos_Typed_id &_tpid,sos_Container ct, sos_Bool key_based_on_equal, sos_Bool list_cursor, sos_Offset page_list_offset, sos_Int org_hash_value, sos_Object key, sos_Object info, sos_Object pred, sos_Object succ) // ************************************************************************** // traegt einen Eintrag in eine Seitenliste ein und ersetzt // geg. einen alten. Auf der Seitenliste muss Platz sein { T_PROC("Mapping::insert_in_page_list"); page_t page; sos_Offset page_offset = page_list_offset; // Befindet sich der key bereits auf der Seite, so wird er durch // den neuen Eintrag ersetzt. Schaue also nach sos_Int pos = -1; // entspricht "nicht gefunden" sos_Int entries_on_page = MAX_PAGE_ENTRIES(list_cursor); page.page_header = read_page_header(ct,page_offset); page_header_t first_page_header = page.page_header; sos_Int pages = first_page_header.pages; sos_Int entries_on_last_page = first_page_header.entries_on_last_page; for (sos_Int page_no=1; (page_no <= pages) AND (pos == -1); page_no++) { if (page_no == pages) entries_on_page = entries_on_last_page; read_page(list_cursor, ct,page_offset, entries_on_page, page); pos = search_page_for_key (key_based_on_equal, page, entries_on_page, org_hash_value,key); if ((pos == -1) AND (page_no < pages)) page_offset = page.page_header.next_page; } // Falls der Eintrag ersetzt wird, so wird er an diese // Stelle eingesetzt. Ansonsten wird er auf der // letzten Seite eingetragen und die H-Tabelle korrigiert sos_Int write_pos = pos; if (pos == -1) { // Eintrag nicht gefunden, also schreibe ihn auf die letzte Seite. // Da die gesamte Liste durchgegangen wurde, ist page_offset // die Adresse der letzten Seite. // korrigiere Seitenheader der ersten Seite first_page_header.entries_on_last_page++; write_page_header(ct,page_list_offset,first_page_header); write_pos = entries_on_page; sos_Object_sos_Object_Mapping::make(_tpid,this).set_cardinality(sos_Object_sos_Object_Mapping::make(_tpid,this).get_cardinality() + 1); } page.entry[write_pos].hash_value = org_hash_value; // Falls der Key existiert wird er nicht nochmal eingetragen, // sondern nur der Entity ersetzt if (write_pos != pos) page.entry[write_pos].key = make_object_save(key); page.entry[write_pos].info = make_object_save(info); // Schreibe die Liste evt auf die Seite zurueck if (pos == -1) { if (list_cursor) { // schreibe in die Liste succ und pred page.list[write_pos].succ = make_object_save(succ); page.list[write_pos].pred = make_object_save(pred); // aktualisiere den Vorwaertszeiger von pred und den // Rueckwaertszeiger von succ sos_Object_sos_Object_Mapping::make(_tpid,this).write_succ_pred (ct, key_based_on_equal, list_cursor, pred, TRUE, key); sos_Object_sos_Object_Mapping::make(_tpid,this).write_succ_pred (ct, key_based_on_equal, list_cursor, succ, FALSE, key); if (sos_Object_sos_Object_Mapping::make(_tpid,this).get_cardinality() == 1) { // erstes Element wird eingefuegt sos_Object_sos_Object_Mapping::make(_tpid,this).set_first_object(key); sos_Object_sos_Object_Mapping::make(_tpid,this).set_last_object(key); } else { if (sos_Object_sos_Object_Mapping::make(_tpid,this).get_first_object() == succ) sos_Object_sos_Object_Mapping::make(_tpid,this).set_first_object(key); if (sos_Object_sos_Object_Mapping::make(_tpid,this).get_last_object() == pred) sos_Object_sos_Object_Mapping::make(_tpid,this).set_last_object(key); } } // schreibe den Eintrag auf der Seite zurueck write_page_entry(list_cursor, ct,page_offset, write_pos, page); } else // Der Eintrag war bereits vorhanden, nur der Entity wurde geaendert // schreibe den Eintrag auf der Seite zurueck write_page_entry(list_cursor, ct,page_offset, write_pos, page); TT(agg_M, T_LEAVE); } // ** Mapping::insert_in_page_list ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::split_page(sos_Typed_id &_tpid,sos_Container ct, sos_Bool list_cursor, sos_Offset page_list_offset, sos_Char par_local_depth, sos_Int org_hash_value, sos_Int nec_local_depth) // ************************************************************************** // Eine Seitenliste wird solange gesplittet, bis der Seitenliste // die neue lokale Tiefe nec(cessary)_local_depth zugeordnet wird. // Saemtliche Seitenteilungen vor der letzten sind Seitenteilungen // ohne Bewegungen von Objekten, denn wuerden Objekte bewegt, // wuerde Platz geschaffen und somit waere nec_local_depth kleiner. { T_PROC("Mapping::split_page"); TT(agg_M, T_ENTER); sos_Int global_depth = sos_Object_sos_Object_Mapping::make(_tpid,this).get_global_depth(); sos_Offset no_of_pages_offset = sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_pages_offset(); sos_Offset ht_offset = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(); sos_Int max_page_entries = MAX_PAGE_ENTRIES(list_cursor); // Teile die Seitenliste sooft wie noetig, d.h. allokiere neue // Seiten und biege die richtigen HT-Eintraege auf diese // Seiten um for (;par_local_depth <nec_local_depth;) { // erzeuge neue leere Seite ht_entry_t new_ht_entry; new_ht_entry.page_list_offset = new_page_list(ct, list_cursor); sos_Int local_depth = par_local_depth; sos_Int new_local_depth = ++par_local_depth; new_ht_entry.local_depth = new_local_depth; sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_page_lists(sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_page_lists() + 1); sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_pages(sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_pages() + 1); add_no_of_pages(ct,no_of_pages_offset,local_depth,-1); add_no_of_pages(ct,no_of_pages_offset,new_local_depth,2); // biege alle Eintraege der HT mit gesetztem // new_local_depth.ten Bit auf die neue Seite um // Wie lautet der der neuen Seite zugeordnete Hash-Wert-Teil ? // Nimm den alten Hash-Teil sos_Int old_hash_value_part = org_hash_value BITAND (LSHIFT(1,local_depth)-1); // Laufe alle Indexe durch, bei denen ein Verweis // auf die zu teilende Seitenliste steht. Das sind // 2^(global_depth-local_depth) Stueck. // Die Verweise werden abwechselnd belassen und umgebogen. // Da auf der alten Seitenliste die ersten nec_local_depth // Bit gleich sind, wird so begonnen, dass die alte Seitenliste // immer noch von der gleichen Stelle der // H-Tabelle erreicht wird (new_link) sos_Bool new_link = FALSE; // starte bei alter Seitenliste if ((org_hash_value BITAND LSHIFT(1,local_depth)) != 0) new_link = TRUE; // starte bei leerer Seite // wo ist der erste Verweis auf diese Seitenliste ? for (sos_Int k = 0;k<LSHIFT(1,global_depth-local_depth); k++) { // verschiebe Laufindex k an richtige Position // vor dem Wert der Seite sos_Int j = LSHIFT(k,local_depth); // addiere den Hash-Teil der Seite dazu j = j BITOR old_hash_value_part; if (new_link) // Dieser Eintrag muss auf die neue // leere Seite umgebogen werden write_ht_entry(ct, ht_offset, j, new_ht_entry); else { // Dieser Eintrag verbleibt, // lediglich die lokale Tiefe wird geaendert ht_entry_t tmp_ht_entry = read_ht_entry(ct, ht_offset, j); tmp_ht_entry.local_depth ++; write_ht_entry(ct,ht_offset,j, tmp_ht_entry); } new_link = (sos_Bool) (NOT new_link); } } // Da nur die letzte Seitenteilung hierbei beteiligt ist, // muessen also Seiteneintraege der alten Seitenliste // auf die bis jetzt leere Partnerseite. // Berechne Hash-Teil der alten Seitenliste sos_Int hash_value_part = org_hash_value BITAND (LSHIFT(1,nec_local_depth)-1); // invertiere das nec_local_depth.te Bit => Partnerseite sos_Int ht_partner_pos = hash_value_part BITXOR LSHIFT(1,nec_local_depth-1); // Lese den Offset der Partnerseite ht_entry_t ht_partner_entry = read_ht_entry(ct,ht_offset,ht_partner_pos); // Also: Die Eintraege kommen von ht_entry.page_list_offset // nach ht_partner_entry.page_list_offset // Gehe die alte Seitenliste durch und schiebe jeden Seiteneintrag, // dessen Hashwert ein anderes nec_local_depth.tes Bit aufweist // als in org_hash_value, auf die neue Seitenliste sos_Offset old_page_offset = page_list_offset; sos_Offset read_page_offset = old_page_offset; sos_Offset new_page_offset = ht_partner_entry.page_list_offset; sos_Int bit = LSHIFT(1,nec_local_depth-1); sos_Int should_be_bit = org_hash_value BITAND bit; page_t old_page; page_header_t old_page_header = read_page_header(ct,old_page_offset); old_page.page_header = old_page_header; sos_Int old_entry_no = 0; sos_Int old_page_no = 1; sos_Bool add_old_page = FALSE; page_t new_page; sos_Int new_entry_no = 0; sos_Int new_page_no = 1; page_header_t new_page_header = read_page_header(ct,new_page_offset); new_page.page_header = new_page_header; page_t r_page; page_header_t r_page_header = read_page_header(ct,read_page_offset); sos_Int no_of_pages = sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_pages(); for (sos_Int read_page_no=1; read_page_no <= r_page_header.pages; read_page_no++) { sos_Int read_entries = (r_page_header.pages > read_page_no)? max_page_entries:r_page_header.entries_on_last_page; read_page(list_cursor, ct,read_page_offset, read_entries, r_page); read_page_offset = r_page.page_header.next_page; // Gehe die alte Seite durch und kopiere die passenden Eintraege // auf die neue Seite. for (sos_Int read_no=0;read_no<read_entries;read_no++) { if ((r_page.entry[read_no].hash_value BITAND bit) !=should_be_bit) { // gesetztes Bit => Eintrag auf neue Seite // Ist neue Seite schon vollstaendig beschrieben ? if (new_entry_no == max_page_entries) { // Falls die neue Seite bereits voll ist, erweitere Liste sos_Offset last_page = increase_page_list(list_cursor, ct, ht_partner_entry.page_list_offset, new_page_header, new_page_offset); no_of_pages++; new_page.page_header = new_page_header; new_page.page_header.next_page = last_page; // und schreibe die volle Seite aus write_page(list_cursor, ct, new_page_offset, max_page_entries, new_page); new_entry_no = 0; new_page_offset = last_page; new_page_no++; } new_page.entry[new_entry_no] = r_page.entry[read_no]; // Falls list_cursor == FALSE schadet // die folgende Zeile nichts new_page.list[new_entry_no++] = r_page.list[read_no]; } else { // Ist die alte Seite schon vollstaendig beschrieben ? if (old_entry_no == max_page_entries) { // Ja, schreibe alte volle Seite aus write_page(list_cursor, ct, old_page_offset, max_page_entries, old_page); old_page_no++; old_page_offset = old_page.page_header.next_page; // Lese den Vorwaertsverweis der naechsten Seite if (old_page_no < old_page_header.pages) old_page.page_header=read_page_header(ct,old_page_offset); old_entry_no = 0; } old_page.entry[old_entry_no] = r_page.entry[read_no]; // Falls list_cursor == FALSE schadet // die folgende Zeile nichts old_page.list[old_entry_no++] = r_page.list[read_no]; } } } sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_pages(no_of_pages); // Falls eine neue Seite noch nicht ausgeschrieben ist, tue dies if (new_entry_no > 0) { new_page.page_header.entries_on_last_page = new_entry_no; new_page.page_header.pages = new_page_no; write_page(list_cursor, ct,new_page_offset, new_entry_no, new_page); } // Korrigiere Seitenheader der ersten neuen Seite new_page_header.entries_on_last_page = new_entry_no; new_page_header.pages = new_page_no; // Schreibe evt. den Seitenheader if ((new_page_header.pages > 1) OR (new_entry_no == 0)) write_page_header(ct,ht_partner_entry.page_list_offset,new_page_header); // Falls eine alte Seite noch nicht ausgeschrieben wurde, tue dies if (old_entry_no > 0) { old_page.page_header.entries_on_last_page = old_entry_no; old_page.page_header.pages = old_page_no; write_page(list_cursor, ct,old_page_offset, old_entry_no, old_page); } // Falls die alte Seitenliste eine volle Liste ist, lasse eine // leere Seite dranhaengen, da auf der alten Seitenliste ein // neuer Eintrag erfolgen soll. if (old_entry_no == max_page_entries) { old_entry_no = 0; old_page_no++; add_old_page = TRUE; } // Korrigiere Seitenheader der ersten alten Seite old_page_header.entries_on_last_page = old_entry_no; old_page_header.pages = old_page_no; // Schreibe evt. alten Seitenheader if ((old_page_header.pages > 1) OR (old_entry_no == 0)) write_page_header(ct,page_list_offset, old_page_header); // Gebe den Rest der alten Seitenliste frei if (old_page_no < r_page_header.pages) { destroy_page_list(list_cursor, ct, old_page.page_header.next_page, r_page_header.pages-old_page_no); sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_pages(no_of_pages - (r_page_header.pages - old_page_no)); } TT(agg_M, T_LEAVE); } // ** Mapping::split_page ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::increase_hash_table(sos_Typed_id &_tpid) // ************************************************************************** // erweitere die gesamte Hash-Tabelle, d.h. allokiere // doppelt soviel Platz wie jetzt belegt wird, kopiere // die alte Hash-Tabelle in die erste Haelfte und danach // in die zweite Haelfte. => Auf jede Seite // existieren zwei Verweise. Wirf die alte Hashtabelle weg { T_PROC("Mapping::increase_hash_table"); TT(agg_M, T_ENTER); sos_Container ct = sos_Object_sos_Object_Mapping::make(_tpid,this).container(); sos_Int old_ht_entries = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(); sos_Int old_ht_size = old_ht_entries*HT_ENTRY_SIZE; sos_Offset new_ht_offset = ct.allocate(old_ht_size*2); // kopiere bisherige Hashtabelle zweimal // hintereinander in den neu allokierten Platz ct.copy(sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(), old_ht_size, ct, new_ht_offset); ct.copy(sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(), old_ht_size, ct, new_ht_offset+old_ht_size); // Wirf alte Hash-tabelle weg ct.deallocate(sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(), old_ht_size); // markiere die Neue sos_Object_sos_Object_Mapping::make(_tpid,this).set_ht_offset(new_ht_offset); sos_Object_sos_Object_Mapping::make(_tpid,this).set_ht_entries(old_ht_entries * 2); sos_Object_sos_Object_Mapping::make(_tpid,this).set_global_depth(sos_Object_sos_Object_Mapping::make(_tpid,this).get_global_depth() + 1); TT(agg_M, T_LEAVE); } // ** Mapping::increase_hash_table ** // increase_ht_structur__F8sos_Booliiiiii // ************************************************************************** sos_Bool increase_ht_structur(sos_Bool list_cursor, sos_Int global_depth, sos_Int ht_entries, sos_Int no_of_pages, sos_Int length, sos_Int local_depth, sos_Int nec_local_depth) // ************************************************************************** // entscheidet, ob wegen eines Seitenueberlaufs die HT-Struktur // vergoessert werden soll, oder an die Seitenliste eine Seite // angehaengt wird. { T_PROC("increase_ht_structur"); TT(agg_M, T_ENTER); // Falls die HT nicht vergroessert werden muesste, sondern // eine Seitenteilung reichen wuerde, plaediere immer fuer die // Erweiterung der HT if (global_depth >= nec_local_depth) { TT(agg_M, T_LEAVE) return TRUE; } // Berechne den Platzbedarf bei Vergroesserung der HT-Struktur sos_Int page_size = PAGE_SIZE(list_cursor); sos_Int ht_memory; // Bei HT-Erweiterung sos_Int pl_memory; // Bei Seitenlisten Erweiterung // Bei normaler Speicherausnutzung, d.h. Jede Seite ist // zur Haelfte gefuellt, gibt die Kennzahl 100, sonst > 100 // Ist die Ausnutzung besser, gilt Kennzahl < 100 // Bedarf der jetzigen Hashtabelle sos_Int ht_use = ht_entries*HT_ENTRY_SIZE; // Bedarf der jetzigen saemtlichen Seiten sos_Int page_lists_use = no_of_pages*page_size; // Berechne den Speicher, der beim Erweitern der HT-Struktur // zusaetzlich benoetigt wird // Fuer jedes Splitten kommt eine Seite dazu sos_Int increasing_use = (nec_local_depth-local_depth)*page_size; // Fuer das Erweitern der HT kommt immer die bisherige Groesse dazu increasing_use += (LSHIFT(1,nec_local_depth) - LSHIFT(1,global_depth)) *HT_ENTRY_SIZE; sos_Int ht_memory_used = ht_use + page_lists_use + increasing_use; // Bei der Alternative der Seitenliste kommt nur eine Seite dazu; sos_Int pl_memory_used = ht_use + page_lists_use + page_size; // Berechne den optimalen Platzbedarf. Er ergibt sich durch // zu drei/viertel gefuellte Seiten, wobei jeder Verweis auf genau // eine Seite zeigt.=> Berechne Anzahl der noetigen Seiten = Verweise, // plumitiziere mit Platzbedarf sos_Int opt_entries = MAX_PAGE_ENTRIES(list_cursor)*3/4; sos_Int opt_memory_used = (page_size + HT_ENTRY_SIZE)* length/opt_entries; ht_memory = (ht_memory_used*100) / opt_memory_used; pl_memory = (pl_memory_used*100) / opt_memory_used; // Berechne die Kennzahl der Zeiteffizienz // Es wird die durchschnittliche Laenge einer Seitenliste berechnet // Falls keine Seitenlisten existieren, so ist die Effizienz // optimal. Die HT-Erweiterung ist in diesem Sinne optimal, auch wenn // durch fruehere Seitenlisten die Effizienz nicht 1000 entspricht, // ist der einzige moegliche Weg dorthin die HT-Erweiterung. sos_Int ht_eff = 100; sos_Int pl_eff = 100*(no_of_pages+1)/ht_entries; // Jetzt kommt die Entscheidung: Beide Kennzahlen // werden gleich gewichtet und verglichen sos_Bool result = FALSE; if ((ht_memory + ht_eff) <= (pl_memory + pl_eff)) result = TRUE; TT(agg_M, T_LEAVE); return result; } // ** Mapping::increase_ht_structur ** // ************************************************************************** sos_Int neccessary_local_depth(sos_Bool list_cursor, sos_Container ct, sos_Bool key_based_on_equal, sos_Offset page_list_offset, sos_Char local_depth, sos_Offset& last_page_offset, sos_Int org_hash_value, sos_Object key) // ************************************************************************** // Es wird berechnet, welche lokale Tiefe eine volle Seitenliste // plus dem einzufuegenden Objekt haben muesste, damit mindestens // ein Seiten Eintrag abgespalten // werden koennte. Oder: Es wird berechnet, bis zu welchem // Bit im Hash-Wert nicht mehr alle Seiteneintraege gleich sind // Wird dagegen ein Eintrag gefunden, der durch den neuen Eintrag // ersetzt wuerde, so wird dieselbe lokale Tiefe zurueckgeliefert { T_PROC("neccessary_local_depth"); TT(agg_M, T_ENTER); sos_Int hash_value = org_hash_value; sos_Int nec_local_depth = MAX_GLOBAL_DEPTH; sos_Offset page_offset = page_list_offset; page_header_t first_page_header; first_page_header = read_page_header(ct, page_list_offset); sos_Int pages = first_page_header.pages; sos_Int entries_on_last_page = first_page_header.entries_on_last_page; sos_Int entries_on_page = MAX_PAGE_ENTRIES(list_cursor); // Durchlaufe alle Seiten for (sos_Int page_no=1; (page_no<=pages); page_no++) { // Diese Schleife darf nicht vorher abgebrochen werden, da // last_page_offset als Ausgabeparameter erwartet wird page_t page; if (page_no == pages) entries_on_page = entries_on_last_page; read_page(list_cursor, ct, page_offset,entries_on_page, page); last_page_offset = page_offset; page_offset = page.page_header.next_page; // Durchlaufe alle Eintraege auf dieser Seite for (sos_Int entry_no = 0; (entry_no < entries_on_page); entry_no++) { sos_Int tmp_hash_value = page.entry[entry_no].hash_value; if (org_hash_value == tmp_hash_value) { sos_Object o = make_Object(page.entry[entry_no].key); if (agg_same_entity (key, o,key_based_on_equal, EQ_STRONG)) { // das Objekt wuerde dieses Objekt ersetzen, also // keine Erhoehung der lokalen Tiefe notwendig TT(agg_M, T_LEAVE); return local_depth; } } // Ab dem wievielten Bit unterscheiden sich der bisherige // Hashwert und der aktuelle Eintrag ? sos_Int diff_hash_value = hash_value BITXOR tmp_hash_value; // Die Bits der lokalen Tiefe sind auf alle Faelle gleich, // also rechne erst ab local_depth+1; for (sos_Int i = local_depth+1; i<=nec_local_depth; i++) if ((RSHIFT(diff_hash_value, i-1) BITAND 1) == 1) nec_local_depth = i++; } } TT(agg_M, T_LEAVE); return nec_local_depth; } // ** Mapping::neccessary_local_depth * // insert_in_list__30_sos_Object_sos_Object_MappingR12sos_Typed_idG10sos_ObjectN32 // ************************************************************************** void _sos_Object_sos_Object_Mapping::insert_in_list (sos_Typed_id &_tpid,sos_Object key, sos_Object info, sos_Object pred, sos_Object succ) // ************************************************************************** // fuegt einen Eintrag ein und setzt ihn, falls list_cursor == TRUE // in die durch pred und succ gegebenene Reihenfolge { T_PROC("Mapping::insert_in_list"); TT(agg_M, T_ENTER); // Ist das Mapping leer, so existiert noch keine Hashtabelle if (sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries() == 0) sos_Object_sos_Object_Mapping::make(_tpid,this).init_table(); sos_Container ct = sos_Object_sos_Object_Mapping::make(_tpid,this).container(); sos_Bool key_based_on_equal = sos_Object_sos_Object_Mapping::make(_tpid,this).get_key_based_on_equal(); sos_Bool list_cursor = sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(); sos_Int org_hash_value = absolute((key_based_on_equal)? key.hash_value():hash_id(key)); sos_Int ht_pos = org_hash_value MOD sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(); sos_Offset ht_offset = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(); ht_entry_t ht_entry = read_ht_entry(ct,ht_offset,ht_pos); page_header_t page_header = read_page_header(ct,ht_entry.page_list_offset); // Ist die Seitenliste aufgefuellt ? if (page_header.entries_on_last_page < MAX_PAGE_ENTRIES(list_cursor)) // Nein, es ist noch Platz sos_Object_sos_Object_Mapping::make(_tpid,this).insert_in_page_list(ct, key_based_on_equal, list_cursor, ht_entry.page_list_offset, org_hash_value, key, info, pred, succ); else { // Die Seitenliste ist voll. Entweder wird die HT erweitert, // oder die Seitenliste wird um eine Seite vergroessert. sos_Offset last_page =0; sos_Int nec_local_depth = neccessary_local_depth( list_cursor, ct, key_based_on_equal, ht_entry.page_list_offset, ht_entry.local_depth, last_page, org_hash_value, key); // Falls der Eintrag einen anderen ersetzt, so // ist keine Erweiterung notwendig if (nec_local_depth == ht_entry.local_depth) sos_Object_sos_Object_Mapping::make(_tpid,this).insert_in_page_list(ct, key_based_on_equal, list_cursor, ht_entry.page_list_offset, org_hash_value, key, info, pred, succ); else { if (increase_ht_structur (list_cursor, sos_Object_sos_Object_Mapping::make(_tpid,this).get_global_depth(),sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_pages(),sos_Object_sos_Object_Mapping::make(_tpid,this).get_cardinality(), ht_entry.local_depth, nec_local_depth)) { // Die Hash-Struktur wird vergroessert, erweitere // zuerst die Hashtabelle auf die benoetigten Groesse for (;sos_Object_sos_Object_Mapping::make(_tpid,this).get_global_depth() < nec_local_depth;) sos_Object_sos_Object_Mapping::make(_tpid,this).increase_hash_table(); // veraendert get_global_depth() ! // Teile jetzt die Seiten. Hierbei sind alle Seitenteilungen // ohne Objektumschaufelungen verbunden, bis auf die letzte. // Drum der Parameter nec_local_depth sos_Object_sos_Object_Mapping::make(_tpid,this).split_page (ct, list_cursor, ht_entry.page_list_offset, ht_entry.local_depth, org_hash_value, nec_local_depth); ht_pos = org_hash_value MOD sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(); ht_entry = read_ht_entry(ct,sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(), ht_pos); sos_Object_sos_Object_Mapping::make(_tpid,this).insert_in_page_list (ct, key_based_on_equal, list_cursor, ht_entry.page_list_offset, org_hash_value, key, info, pred, succ); } else { // Die Seitenliste wird um eine Seite erweitert: increase_page_list(list_cursor, ct, ht_entry.page_list_offset, page_header, last_page); sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_pages(sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_pages()+1); sos_Object_sos_Object_Mapping::make(_tpid,this).insert_in_page_list(ct, key_based_on_equal, list_cursor, ht_entry.page_list_offset,org_hash_value, key, info, pred, succ); } } } TT (agg_M,T_LEAVE); } // ** Mapping:insert_in_list ** // remove_from_page_list__30_sos_Object_sos_Object_MappingR12sos_Typed_idG13sos_Container8sos_BoolT3UiiG10sos_Object // ************************************************************************** sos_Object _sos_Object_sos_Object_Mapping::remove_from_page_list (sos_Typed_id &_tpid,sos_Container ct, sos_Bool list_cursor, sos_Bool key_based_on_equal, sos_Offset page_list_offset, sos_Int org_hash_value, sos_Object key) // ************************************************************************** // Liefert sos_Bool_Object::TRUE, falls der Eintrag gefunden wurde. Der Entry // wird nicht geliefert, da er sonst mit make_Object angefasst werden muesste, // was er hier nicht soll. { T_PROC("Mapping::remove_from_page_list"); TT(agg_M, T_ENTER); sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); // assume that key won't be found: sos_Object found = make_sos_Bool_object(FALSE); page_t page; page_header_t page_header; sos_Offset page_offset = page_list_offset; sos_Offset prev_page_offset; page_header = read_page_header(ct, page_offset); sos_Int pages = page_header.pages; sos_Int entries_on_last_page = page_header.entries_on_last_page; sos_Int entries_on_page = MAX_PAGE_ENTRIES(list_cursor); sos_Int page_no = 1; sos_Int pos = -1; // entspricht "nicht gefunden"; for (; (page_no<=pages) AND (pos == -1); page_no++) { if (page_no == pages) entries_on_page = entries_on_last_page; read_page(list_cursor, ct, page_offset, entries_on_page, page); pos = search_page_for_key (key_based_on_equal, page, entries_on_page, org_hash_value,key); if (pos == -1) { prev_page_offset = page_offset; page_offset = page.page_header.next_page; } } page_no --; // Im Falle von based_on_equal == TRUE ist der key nicht unbedingt // derselbe wie der tatsaechlich eingetragene Schluessel. real_key // ist der Eingetragene. real_key ist fuer den Test auf Identitaet // mit dem ersten bzw. letzten Element notwendig.(Equal ginge auch, // dauert aber laenger). sos_Object real_key; if (pos != -1) { found = make_sos_Bool_object(TRUE); real_key = make_Object(page.entry[pos].key); sos_Object pred,succ; if (list_cursor) { pred = make_Object(page.list[pos].pred); succ = make_Object(page.list[pos].succ); } sos_Object_sos_Object_Mapping::make(_tpid,this).set_cardinality(sos_Object_sos_Object_Mapping::make(_tpid,this).get_cardinality() - 1); // Der zu loeschende Eintrag ist auf der letzten Seite if (page_no == page_header.pages) { // fuelle die Luecke auf if (page_header.entries_on_last_page > 1) { // und korrigiere den Seitenheader page.entry[pos] = page.entry[--page_header.entries_on_last_page]; page.list[pos] = page.list[page_header.entries_on_last_page]; // schreibe Seiteneintrag zurueck write_page_entry(list_cursor, ct, page_offset, pos, page); } else // oder deallokiere die Seite { if (page_header.pages > 1) { ct.deallocate(page_offset, PAGE_SIZE(list_cursor)); page_header.pages --; page_header.entries_on_last_page=MAX_PAGE_ENTRIES(list_cursor); sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_pages(sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_pages()-1); } else page_header.entries_on_last_page = 0; } // schreibe Seitenheader zurueck write_page_header(ct,page_list_offset, page_header); } else { // Falls der Eintrag nicht auf der letzten Seite war, so nimm // einen Eintrag der letzten Seite und schreibe ihn in die Luecke // hangel dich zur letzten Seite vor sos_Offset last_page_offset = page.page_header.next_page; page_header_t tmp_page_header; page_no++; for (;page_no<page_header.pages;page_no++) { tmp_page_header = read_page_header(ct, last_page_offset); last_page_offset = tmp_page_header.next_page; } // nimm von der letzten Seite den letzten Eintrag // und korrigiere nur den Seitenheader page_t tmp_page; read_page_entry(list_cursor, ct,last_page_offset, --page_header.entries_on_last_page, tmp_page); // und fuege ihn in die Luecke page.entry[pos] = tmp_page.entry[page_header.entries_on_last_page]; page.list[pos] = tmp_page.list[page_header.entries_on_last_page]; write_page(list_cursor, ct,page_offset, pos+1, page); // Falls die letzte Seite jetzt leer ist, deallokiere if (page_header.entries_on_last_page == 0) { ct.deallocate(last_page_offset, PAGE_SIZE(list_cursor)); page_header.entries_on_last_page = MAX_PAGE_ENTRIES(list_cursor); page_header.pages--; sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_pages(sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_pages()-1); } // Schreibe den Seitenheader der ersten Seite zurueck write_page_header(ct,page_list_offset, page_header); } if (list_cursor) { // fuege Objekt aus der Liste sos_Object_sos_Object_Mapping::make(_tpid,this).write_succ_pred (ct, key_based_on_equal, list_cursor, pred, TRUE, succ); sos_Object_sos_Object_Mapping::make(_tpid,this).write_succ_pred (ct, key_based_on_equal, list_cursor, succ, FALSE, pred); if (real_key == sos_Object_sos_Object_Mapping::make(_tpid,this).get_last_object()) sos_Object_sos_Object_Mapping::make(_tpid,this).set_last_object(pred); if (real_key == sos_Object_sos_Object_Mapping::make(_tpid,this).get_first_object()) sos_Object_sos_Object_Mapping::make(_tpid,this).set_first_object(succ); // Letztes Objekt: if (sos_Object_sos_Object_Mapping::make(_tpid,this).get_cardinality() == 0) { sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); sos_Object_sos_Object_Mapping::make(_tpid,this).set_first_object(my_no_object); sos_Object_sos_Object_Mapping::make(_tpid,this).set_last_object(my_no_object); } } } TT(agg_M, T_LEAVE); return found; } // ** Mapping::remove_from_page_list ** // ************************************************************************** sos_Bool _sos_Object_sos_Object_Mapping::assemble_page( sos_Typed_id &_tpid,sos_Offset page_list_offset, sos_Char local_depth, sos_Int org_hash_value) // ************************************************************************** { T_PROC("Mapping::assemble_page"); TT(agg_M, T_ENTER); sos_Container ct = sos_Object_sos_Object_Mapping::make(_tpid,this).container(); sos_Bool list_cursor = sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(); sos_Int max_page_entries = MAX_PAGE_ENTRIES(list_cursor); page_t page; page_t partner_page; ht_entry_t ht_partner_entry; page.page_header = read_page_header(ct, page_list_offset); sos_Int ht_partner_pos; sos_Bool assemble_flag; // liefere zurueck, ob verschmolzen wurde sos_Bool result = FALSE; sos_Bool act_page_is_read = FALSE; do // while (assemble_flag) { assemble_flag = FALSE; // zuerst eine Grob-Pruefung, ob eine Chance auf // Verschmelzung besteht: // Ist die Seitenliste nur eine Seite, und ist sie nur 3/4 voll, // und gibt es ueberhaupt eine Partnerseite ? if ((page.page_header.entries_on_last_page <= max_page_entries*3/4) AND (page.page_header.pages == 1) AND (local_depth > 0)) { // Berechne die Position der Partnerseite ht_partner_pos = (org_hash_value BITAND LSHIFT(1,local_depth)-1) BITXOR LSHIFT(1,local_depth-1); // Lese den HT-Eintrag der Partnerseite ht_partner_entry = read_ht_entry(ct,sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(),ht_partner_pos); // Hat die Partnerseite die gleiche lokale Tiefe ? if (ht_partner_entry.local_depth == local_depth) { // Ja // Lese Seitenkopf der Partner Seite partner_page.page_header = read_page_header(ct, ht_partner_entry.page_list_offset); // Besteht die Partnerseite auch nur aus einer Seite ? if (partner_page.page_header.pages == 1) { // passen alle Eintraege gut auf eine Seite ? (gut = 25% frei) assemble_flag = FALSE; if ((partner_page.page_header.entries_on_last_page+ page.page_header.entries_on_last_page) <= max_page_entries*3/4) assemble_flag = TRUE; } } } if (assemble_flag) { result = TRUE; sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_page_lists(sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_page_lists() - 1); if (NOT (act_page_is_read)) { read_page(list_cursor, ct,page_list_offset, page.page_header.entries_on_last_page, page); act_page_is_read = TRUE; } // Lese Partner-Seite read_page(list_cursor, ct,ht_partner_entry.page_list_offset, partner_page.page_header.entries_on_last_page, partner_page); // Kopiere die Eintraege der Partnerseite // auf die urspruengliche Seite for (sos_Int i=0; i<partner_page.page_header.entries_on_last_page;i++) { page.entry[page.page_header.entries_on_last_page] = partner_page.entry[i]; page.list[page.page_header.entries_on_last_page++] = partner_page.list[i]; } // Korrigiere die Angabe, wieviel Seiten mit einer // bestimmten lokalen Tiefe existieren sos_Int no_of_pages_offset = sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_pages_offset(); add_no_of_pages(ct,no_of_pages_offset,local_depth,-2); add_no_of_pages(ct,no_of_pages_offset,local_depth-1,1); // Gib die Partnerseite frei ct.deallocate (ht_partner_entry.page_list_offset, PAGE_SIZE(list_cursor)); sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_pages(sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_pages()-1); write_page(list_cursor, ct,page_list_offset, page.page_header.entries_on_last_page, page); sos_Int old_local_depth = local_depth --; // Die Hashtabelle muss korrigiert werden: Alle // Verweise auf die Partnerseite muessen auf die verschmolzene // Seite umgebogen werden // Wie lautet der der vereinigten Seite zugeordnete Hashwertteil ? sos_Int hash_value_part = ht_partner_pos BITAND (LSHIFT(1,local_depth)-1); // Laufe alle Indexe durch, die auf die neue // vereinigte Seite zeigen. Diese Verweise werden // umgebogen sos_Int global_depth = sos_Object_sos_Object_Mapping::make(_tpid,this).get_global_depth(); sos_Offset ht_offset = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(); for (sos_Int k = 0;k<LSHIFT(1,global_depth-local_depth); k++) { // verschiebe Laufindex k an richtige Position // vor dem Wert der Seite sos_Int j = LSHIFT(k,local_depth); // addiere hash-Teil der Seite dazu j = j BITOR hash_value_part; // Dieser Eintrag muss auf die neue Seite umgebogen werden // d.h. vielleicht zeigt er schon auf die Seite, aber // die lokale Tiefe ist in jedem Fall zu hoch ht_entry_t ht_entry; ht_entry.page_list_offset = page_list_offset; ht_entry.local_depth = local_depth; write_ht_entry(ct,ht_offset,j, ht_entry); } } } while (assemble_flag); TT(agg_M,T_LEAVE); return result; } // ** Mapping::assemble_page ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::decrease_hash_table(sos_Typed_id &_tpid) // ************************************************************************** // die Hash-tabelle kann genau dann halbiert werden, wenn // keine Seiten existieren, die nur einen Verweis in der // Hash-Tabelle besitzen { T_PROC("Mapping::decrease_hash_table"); TT(agg_M, T_ENTER); sos_Container ct = sos_Object_sos_Object_Mapping::make(_tpid,this).container(); sos_Int global_depth = sos_Object_sos_Object_Mapping::make(_tpid,this).get_global_depth(); sos_Offset no_of_pages_offset = sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_pages_offset(); for (;no_single_referenced_pages(ct,no_of_pages_offset,global_depth);) { // Merke alte Tabellen-Position und Laenge sos_Int old_ht_entries = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(); sos_Int old_ht_size = old_ht_entries*HT_ENTRY_SIZE; sos_Offset old_ht_offset = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(); // definiere neue Tabellen Position und Laenge sos_Int new_ht_entries = old_ht_entries / 2; sos_Int new_ht_size = new_ht_entries*HT_ENTRY_SIZE; sos_Int new_ht_offset = ct.allocate(new_ht_entries*HT_ENTRY_SIZE); sos_Object_sos_Object_Mapping::make(_tpid,this).set_ht_entries(new_ht_entries); sos_Object_sos_Object_Mapping::make(_tpid,this).set_ht_offset(new_ht_offset); // kopiere erste Haelfte der alten Tabelle in neue Tabelle ct.copy( old_ht_offset,new_ht_size,ct,new_ht_offset); global_depth--; // Gib alte Tabelle frei ct.deallocate(old_ht_offset,old_ht_size); } sos_Object_sos_Object_Mapping::make(_tpid,this).set_global_depth(global_depth); TT (agg_M,T_LEAVE); } // ** decrease_hash_table // ************************************************************************** sos_Bool get_entry (sos_Container ct, sos_Bool list_cursor, sos_Bool key_based_on_equal, sos_Int ht_entries, sos_Offset ht_offset, sos_Object& key, sos_Bool return_info, sos_Object& info, sos_Object& pred, sos_Object& succ) // ************************************************************************** // Setzt key und - falls list_cursor == TRUE - pred und succ auf die // key-Objekte im gegebenen Mapping. Ergebnis ist das dem key entsprechende // info. Es gilt agg_same_entity (key-vorher, key-nachher, key_based_on_equal) // Wird return_info== TRUE uebergeben, wird info geliefert, ansonsten nicht. // pred und succ werden nur im Falle von list_cursor == TRUE geliefert. { T_PROC("get_entry"); TT(agg_M,T_ENTER; TI(ht_entries); TI(ht_offset)); if (ht_entries>0) { sos_Int org_hash_value = absolute((key_based_on_equal)? key.hash_value() : hash_id(key)); sos_Int ht_pos = org_hash_value MOD ht_entries; ht_entry_t ht_entry = read_ht_entry(ct,ht_offset,ht_pos); page_t page; sos_Offset page_offset = ht_entry.page_list_offset; sos_Int pos = -1; // entspricht "nicht gefunden" page_header_t page_header = read_page_header (ct, page_offset); sos_Int pages = page_header.pages; sos_Int entries_on_last_page = page_header.entries_on_last_page; sos_Int entries_on_page = MAX_PAGE_ENTRIES(list_cursor); for (sos_Int page_no = 1; (page_no<=pages) AND (pos == -1); page_no++) { if (page_no == pages) entries_on_page = entries_on_last_page; read_page(list_cursor, ct, page_offset, entries_on_page, page); pos = search_page_for_key ( key_based_on_equal, page, entries_on_page, org_hash_value, key ); if (pos == -1) page_offset = page.page_header.next_page; else { if (list_cursor) { pred = make_Object(page.list[pos].pred); succ = make_Object(page.list[pos].succ); sos_Bool test1 = (succ == NO_OBJECT); sos_Bool test2 = (pred == NO_OBJECT); } key = make_Object(page.entry[pos].key); if (return_info) info = make_Object(page.entry[pos].info); TT(agg_M, T_LEAVE); // gefunden ! return TRUE; } } } TT(agg_M,T_LEAVE); // nicht gefunden: return FALSE; } // ** get_entry ** /* Reihenfolge in der Hashtabelle: Die Ordnung bezieht sich immer nur auf diejenigen Verweise in der Hashtabelle, die die ersten Verweise auf eine bestimmte Seitenliste sind. Das Durchlaufen mit einem Cursor funktioniert auf der Version mit list_cursor == FALSE so: Die Cursor-Operatoren wurden ueberlagert, mit dem Oeffnen eines Cursors werden die Prozeduren read_first_object und read_last_object aufgerufen, die das erste und letzte Objekt liefern. open_cursor setzt nun die Variablen first_object und last_object. Beim Durchlaufen mit dem Cursor wird die Hashtabelle solange durchsucht, bis ein Verweis gefunden wird, der der Erste auf die referenzierte Seitenliste ist. Dies ist der Fall, wenn in der Position der Hashtabelle (ht_pos) nur die ersten (lokale_tiefe) Bit gesetzt sind. Auf der Seitenliste werden die Eintraege gemaess der Reihenfolge auf der Seite durchlaufen. Nach dem letzten Eintrag auf der Seitenliste beginnt wieder die Suche in der Hashtabelle nach einem ersten Verweis auf die naechste Seitenliste. */ // ************************************************************************** sos_Object get_pred ( sos_Container ct, sos_Bool key_based_on_equal, sos_Object my_no_object, sos_Int ht_entries, sos_Offset ht_offset, sos_Object key) // ************************************************************************** // Diese Prozedur wird nur aufgerufen, wenn get_list_cursor() == FALSE, // d.h. die Version ohne verkettung der Eintraege ist gefragt. // Es wird das Objekt in der eben geschilderten Reihenfolge nach key // geliefert. { sos_Int org_hash_value =absolute((key_based_on_equal)? key.hash_value() : hash_id(key)); sos_Int ht_pos = org_hash_value MOD ht_entries; sos_Object result; ht_entry_t ht_entry = read_ht_entry(ct,ht_offset,ht_pos); page_t page; sos_Int first_link = FIRST_LINK(ht_pos, ht_entry.local_depth); ht_pos = first_link; sos_Offset page_offset = ht_entry.page_list_offset; page_header_t page_header = read_page_header (ct, page_offset); sos_Int pages = page_header.pages; sos_Int entries_on_last_page = page_header.entries_on_last_page; sos_Int entries_on_page = max_page_entries_without_list; result = my_no_object; sos_Int pos = -1; // entspricht "nicht gefunden" for (sos_Int page_no = 1; (page_no<=pages) AND (pos == -1); page_no++) { if (page_no == pages) entries_on_page = entries_on_last_page; read_page(FALSE, ct, page_offset, entries_on_page, page); pos = search_page_for_key ( key_based_on_equal, page, entries_on_page, org_hash_value, key ); if (pos == -1) { page_offset = page.page_header.next_page; result = make_Object(page.entry[entries_on_page-1].key); } else { // Findet sich der Vorgaenger auf derselben Seite ? if (pos > 0) result = make_Object(page.entry[pos-1].key); else { // Falls eine vorherige Seite in der seitenliste // existiert, so wurde ihr letzter Eintrag in // result bereits gesetzt. Ist es jedoch die erste Seite // muss auf eine vorherige Seitenliste gegangen werden if (page_no == 1) { sos_Bool found = FALSE; do { ht_pos--; do { if (ht_pos >= 0) { // suche den naechsten ersten verweis ab ht_pos; ht_entry = read_ht_entry(ct,ht_offset,ht_pos); first_link = FIRST_LINK(ht_pos, ht_entry.local_depth); if (first_link != ht_pos) ht_pos--; } } while ((first_link != ht_pos) AND (ht_pos >= 0)); if (ht_pos >= 0) { page_header = read_page_header(ct,ht_entry.page_list_offset); // Falls auf dieser Seitenliste ein // Eintrag ist, lese ihn, sonst bleibt found == FALSE if ((page_header.pages > 1) OR (page_header.entries_on_last_page > 0)) { found = TRUE; // lese letzte Seite sos_Int i = read_page(FALSE, ct,page_header, ht_entry, page_header.pages, page); result = make_Object(page.entry[i-1].key); } } } while ((NOT found) AND (ht_pos >= 0)); } } } } return result; } // ** get_pred ** // ************************************************************************** sos_Object get_succ ( sos_Container ct, sos_Bool key_based_on_equal, sos_Object my_no_object, sos_Int ht_entries, sos_Offset ht_offset, sos_Object key) // ************************************************************************** { sos_Int org_hash_value = absolute((key_based_on_equal)? key.hash_value() : hash_id(key)); sos_Int ht_pos = org_hash_value MOD ht_entries; sos_Object result; ht_entry_t ht_entry = read_ht_entry(ct,ht_offset,ht_pos); page_t page; sos_Int first_link = FIRST_LINK(ht_pos,ht_entry.local_depth); ht_pos = first_link; sos_Offset page_offset = ht_entry.page_list_offset; page_header_t page_header = read_page_header (ct, page_offset); sos_Int pages = page_header.pages; sos_Int entries_on_last_page = page_header.entries_on_last_page; sos_Int entries_on_page = max_page_entries_without_list; result = my_no_object; sos_Int pos = -1; // entspricht "nicht gefunden" for (sos_Int page_no = 1; (page_no<=pages) AND (pos == -1); page_no++) { if (page_no == pages) entries_on_page = entries_on_last_page; read_page(FALSE, ct, page_offset, entries_on_page, page); pos = search_page_for_key ( key_based_on_equal, page, entries_on_page, org_hash_value, key ); if (pos == -1) page_offset = page.page_header.next_page; else { // Findet sich der Nachfolger auf derselben Seite ? if (pos < entries_on_page-1) result = make_Object(page.entry[pos+1].key); else { if (page_no < page_header.pages) { read_page(FALSE, ct, page.page_header.next_page, 1, page); result = make_Object(page.entry[0].key); } else { // Der Schluessel ist der letzte Eintrag auf der // Seitenliste, der Nachfolger ist auf der // naechsten Seitenliste sos_Bool found = FALSE; do { ht_pos++; do { // suche den naechsten ersten verweis ab ht_pos; // read ht_entry if (ht_pos < ht_entries) { ht_entry = read_ht_entry(ct,ht_offset,ht_pos); first_link = FIRST_LINK(ht_pos,ht_entry.local_depth); if (first_link != ht_pos) ht_pos++; } } while ((first_link != ht_pos) AND (ht_pos < ht_entries)); if (ht_pos < ht_entries) { // lese den Seitenkopf und das erste // Objekt dieser Seite read_page(FALSE,ct, ht_entry.page_list_offset, 1, page); if ((page.page_header.pages > 1) OR (page.page_header.entries_on_last_page > 0)) { found = TRUE; result = make_Object(page.entry[0].key); } } } while ((NOT found) AND (ht_pos < ht_entries)); } } } } return result; } // ** Mapping::get_succ ** // ************************************************************************** sos_Object _sos_Object_sos_Object_Mapping::search_succ_pred (sos_Typed_id &_tpid,sos_Object key, sos_Int steps) // ************************************************************************** { T_PROC("Mapping::search_succ_pred"); TT(agg_M, T_ENTER; TI (steps)); sos_Container ct = sos_Object_sos_Object_Mapping::make(_tpid,this).container(); sos_Bool key_based_on_equal = sos_Object_sos_Object_Mapping::make(_tpid,this).get_key_based_on_equal(); sos_Bool list_cursor = sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(); sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); sos_Int ht_entries = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(); sos_Offset ht_offset = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(); sos_Object dummy_info; // wird in get_entry nicht angefasst for (;steps != 0;) { sos_Object pred,succ; if (list_cursor) { get_entry(ct, list_cursor, key_based_on_equal, ht_entries,ht_offset, key, FALSE, dummy_info, pred,succ); if (steps > 0) { if (succ == my_no_object) { TT(agg_M, T_LEAVE); return my_no_object; } key = succ; steps--; } else { if (pred == my_no_object) { TT(agg_M, T_LEAVE); return my_no_object; } key = pred; steps++; } } else { if (steps > 0) { succ = get_succ(ct,key_based_on_equal,my_no_object, ht_entries,ht_offset,key); if (succ == my_no_object) { TT(agg_M, T_LEAVE); return my_no_object; } key = succ; steps--; } else { pred = get_pred(ct,key_based_on_equal,my_no_object, ht_entries,ht_offset,key); if (pred == my_no_object) { TT(agg_M, T_LEAVE); return my_no_object; } key = pred; steps++; } } } TT(agg_M,T_LEAVE); return key; } // ** Mapping::search_succ_pred ** // ************************************************************************** sos_Object read_last_object(sos_Container ct, sos_Offset ht_offset, sos_Int length, sos_Object my_no_object, sos_Int ht_entries) // ************************************************************************** { sos_Int ht_pos = ht_entries-1; sos_Bool found = FALSE; if (length == 0) return my_no_object; sos_Object result; ht_entry_t ht_entry; page_t page; page_header_t page_header; sos_Int first_link; do { do { // suche den vorherigen ersten Verweis ab ht_pos rueckwaerts ht_entry = read_ht_entry(ct,ht_offset,ht_pos); first_link = FIRST_LINK(ht_pos,ht_entry.local_depth); if (first_link != ht_pos) ht_pos--; } while (first_link != ht_pos); // lese den page_header um festzustellen, ob auf der // Seitenliste ein Objekt ist page_header = read_page_header(ct,ht_entry.page_list_offset); if ((page_header.pages > 1) OR (page_header.entries_on_last_page > 0)) { found = TRUE; // lese letzte Seite read_page(FALSE,ct ,page_header, ht_entry, page_header.pages, page); result=make_Object(page.entry[page_header.entries_on_last_page-1].key); } else ht_pos--; } while (NOT found); return result; } // ** Mapping::read_last_object ** // ************************************************************************** sos_Object read_first_object(sos_Container ct, sos_Offset ht_offset, sos_Int length, sos_Object my_no_object) // ************************************************************************** { sos_Int ht_pos = 0; sos_Bool found = FALSE; if (length == 0) return my_no_object; sos_Object result; ht_entry_t ht_entry; page_t page; sos_Int first_link; do { do { // suche den naechsten ersten verweis ab ht_pos; // read ht_entry ht_entry = read_ht_entry(ct,ht_offset,ht_pos); first_link = FIRST_LINK(ht_pos,ht_entry.local_depth); if (first_link != ht_pos) ht_pos++; } while (first_link != ht_pos); // lese die Seite und das erste Objekt von diesem Verweis read_page(FALSE, ct, ht_entry.page_list_offset, 1, page); if ((page.page_header.pages > 1) OR (page.page_header.entries_on_last_page > 0)) { found = TRUE; result = make_Object(page.entry[0].key); } else ht_pos++; } while (NOT found); return result; } // ** Mapping::read_first_object ** // ************************************************************************** void destroy_ht (sos_Bool list_cursor, sos_Container ct, sos_Int ht_entries, sos_Int global_depth, sos_Offset ht_offset) // ************************************************************************** { T_PROC("destroy_ht "); TT (agg_M, T_ENTER; TI(ht_entries); TI(global_depth)); // loescht alle Eintrage und die Hashtabelle, ht_entry_t ht_entry; for (sos_Int ht_pos=0;ht_pos < ht_entries;ht_pos++) { ht_entry = read_ht_entry(ct,ht_offset,ht_pos); sos_Int local_depth = ht_entry.local_depth; // Berechne denjenigen Teil des Hashwertes, der auf der // Seitenliste unterschieden wird, dies ist gleichzeitig // der erste Verweis auf die Seitenliste sos_Int hash_value_part = FIRST_LINK(ht_pos, local_depth); // Berechne den letzten Verweis auf diese Seiteliste sos_Int last_link = LSHIFT(LSHIFT(1,global_depth-local_depth)-1, local_depth) BITAND hash_value_part; // es muss der letzte Verweis sein, andernfalls wuerde die Kiste // beim nachfolgenden Lesen der lokalen Tiefe abschmieren, // da die Seite schon deallokiert waere. // Falls der gefundendene Verweis der letzte auf diese Seitenliste // ist, so deallokiere diese if (last_link == ht_pos) { page_header_t page_header = read_page_header(ct,ht_entry.page_list_offset); destroy_page_list(list_cursor, ct, ht_entry.page_list_offset, page_header.pages); } } if (ht_entries>0) ct.deallocate(ht_offset,ht_entries*HT_ENTRY_SIZE); TT(agg_M,T_LEAVE); } // ** Mapping::destroy_ht ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::init_table (sos_Typed_id &_tpid) // ************************************************************************** { T_PROC("Mapping::init_table"); TT(agg_H, T_ENTER); // create a hash-tab // Ein Eintrag besteht aus einem Offset // also einem Zeiger auf eine Seite sos_Object_sos_Object_Mapping::make(_tpid,this).set_ht_entries(1); sos_Container ct = sos_Object_sos_Object_Mapping::make(_tpid,this).container(); sos_Object_sos_Object_Mapping::make(_tpid,this).set_ht_offset(ct.allocate (sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries()*HT_ENTRY_SIZE)); // no_of_pages[i] gibt an, wieviel Seiten mit der lokalen // Tiefe i existieren sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_pages_offset(ct.allocate(NO_OF_PAGES_ARRAY_SIZE)); sos_Char mem [NO_OF_PAGES_ARRAY_SIZE]; for (sos_Int i = 0;i< NO_OF_PAGES_ARRAY_SIZE;i++) mem[i] = 0; ct.write(sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_pages_offset(), NO_OF_PAGES_ARRAY_SIZE, &mem); ht_entry_t ht_entry; ht_entry.page_list_offset = ct.allocate(PAGE_SIZE(sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor())); ht_entry.local_depth = 0; write_ht_entry(ct, sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(),0,ht_entry); page_header_t page_header; page_header.pages = 1; page_header.entries_on_last_page = 0; write_page_header(ct,ht_entry.page_list_offset,page_header); sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_pages(1); sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_page_lists(1); add_no_of_pages(ct,sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_pages_offset(),0,1); TT(agg_H,T_LEAVE); } // ** Mapping::init_table ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::local_initialize (sos_Object_sos_Object_Mapping map) // ************************************************************************** { T_PROC("Mapping::local_initialize"); TT(agg_H, T_ENTER); sos_Object my_no_object = map; map.set_cardinality (0); map.set_first_object(my_no_object); map.set_last_object(my_no_object); map.set_ht_entries(0); map.set_global_depth(0); map.set_no_of_pages_offset(NIL); map.set_no_of_pages(0); map.set_ht_offset(NIL); TT(agg_H,T_LEAVE); }; // ** Mapping::local_initialize ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::local_finalize (sos_Object_sos_Object_Mapping map) // ************************************************************************** { T_PROC("Mapping::local_finalize"); TT (agg_H, T_ENTER); destroy_ht(map.get_list_cursor(), map.container(), map.get_ht_entries(), map.get_global_depth(), map.get_ht_offset()); TT (agg_H, T_LEAVE); } // ** Mapping::local_finalize ** // ************************************************************************** sos_Bool _sos_Object_sos_Object_Mapping::is_key (sos_Typed_id &_tpid,sos_Object key) // ************************************************************************** { T_PROC("Mapping::is_key"); TT(agg_H, T_ENTER); sos_Object dummy_pred,dummy_succ,dummy_info; sos_Bool result = FALSE; sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); result = get_entry(sos_Object_sos_Object_Mapping::make(_tpid,this).container(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_key_based_on_equal(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(), key, FALSE, dummy_info, dummy_pred,dummy_succ); TT(agg_H, T_LEAVE); return result; } // ** Mapping::is_key ** // ************************************************************************** sos_Bool _sos_Object_sos_Object_Mapping::is_info (sos_Typed_id &_tpid,sos_Object info) // ************************************************************************** // TRUE, falls info mit insert(*,info) in die Struktur // aufgenommen wurde. // Vorsicht !! nicht aufrufen !! // Die Funktion hat einen Aufwand von o(Anzahl der Eintraege) !! { T_PROC("Mapping::is_info"); TT(agg_H,T_ENTER); sos_Container ct = sos_Object_sos_Object_Mapping::make(_tpid,this).container(); sos_Bool list_cursor = sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(); sos_Bool info_based_on_equal = sos_Object_sos_Object_Mapping::make(_tpid,this).get_info_based_on_equal(); ht_entry_t ht_entry; page_t page; sos_Object key; // Durchlaufe die gesamte Hashtabelle // benutze jeden Verweis um die Seite zu durchsuchen, // verwende bei mehrfachen Verweisen nur den ersten sos_Int ht_entries = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(); sos_Offset ht_offset = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(); for (sos_Int ht_pos=0; ht_pos < ht_entries; // positiver Abbruch nur in der Schleife ht_pos++) { ht_entry = read_ht_entry(ct,ht_offset,ht_pos); // Wie lautet der dieser Seite zugeordnete Hash-Wert ? sos_Int hash_value_part = (LSHIFT(1,ht_entry.local_depth)-1) BITAND ht_pos; // Ist dieser Verweis der erste auf diese Seite ? if (hash_value_part == ht_pos) { // Ja, sos_Offset page_offset = ht_entry.page_list_offset; page_header_t page_header = read_page_header(ct, page_offset); sos_Bool found = FALSE; sos_Int pages = page_header.pages; sos_Int entries_on_last_page = page_header.entries_on_last_page; sos_Int entries_on_page = MAX_PAGE_ENTRIES(list_cursor); for (sos_Int page_no = 1; (page_no<=pages) AND (NOT found); page_no++) { if (page_no == pages) entries_on_page = entries_on_last_page; if (entries_on_page > 0) read_page(list_cursor, ct,page_offset, entries_on_page, page); // Durchsuche die Seite nach dem Objekt for (sos_Int k=0; (NOT found) AND (k<entries_on_page); k++) { key = make_Object(page.entry[k].info); found = agg_same_entity (key,info,info_based_on_equal,EQ_STRONG); } if (found) { TT(agg_H,T_LEAVE); return TRUE; } page_offset = page.page_header.next_page; } } } TT(agg_H,T_LEAVE); return FALSE; // Pech ... } // ** Mapping::is_info ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::insert(sos_Typed_id &_tpid,sos_Object Key, sos_Object Entity) // ************************************************************************** { T_PROC("Mapping::insert"); TT(agg_H, T_ENTER); sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); // Das Mappingobjekt selbst wird als Methodenausgabe fuer ein ungueltiges // Objekt benutzt. Drum kann man NO_OBJECT, aber nicht das // Mappingobjekt einfuegen. Soll es auch auch eingefuegt werden koennen, // benoetigt man ein eigenes MAPPING_NO_OBJECT. if (Key == sos_Object_sos_Object_Mapping::make(_tpid,this)) err_raise(err_USE,"Mapping itself cannot inserted","Mapping::insert"); // fuege das Element in der Listen-Version hinten an die Liste // in der non-Listen Version haben die beiden letzten Parameter // pred und succ keine Wirkung sos_Object_sos_Object_Mapping::make(_tpid,this).insert_in_list(Key, Entity, sos_Object_sos_Object_Mapping::make(_tpid,this).get_last_object(), my_no_object); TT(agg_H,T_LEAVE); } // ** Mapping::insert ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::remove (sos_Typed_id &_tpid,sos_Object key) // ************************************************************************** { T_PROC("Mapping::remove"); sos_Bool found=FALSE; if (sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries() >0) { sos_Container ct = sos_Object_sos_Object_Mapping::make(_tpid,this).container(); sos_Int org_hash_value = absolute((sos_Object_sos_Object_Mapping::make(_tpid,this).get_key_based_on_equal())? key.hash_value():hash_id(key)); sos_Int ht_pos = org_hash_value MOD sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(); ht_entry_t ht_entry = read_ht_entry(ct,sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(),ht_pos); found = make_sos_Bool( sos_Object_sos_Object_Mapping::make(_tpid,this).remove_from_page_list (ct, sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_key_based_on_equal(), ht_entry.page_list_offset, org_hash_value,key)); sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); if (found) { // Versuche, die Seitenliste zu verschmelzen if (sos_Object_sos_Object_Mapping::make(_tpid,this).assemble_page(ht_entry.page_list_offset, ht_entry.local_depth, org_hash_value)) // versuche, die Hash-Tabelle zu verkleinern sos_Object_sos_Object_Mapping::make(_tpid,this).decrease_hash_table(); } } TT(agg_H,T_LEAVE; TB(found)); }// ** Mapping::remove ** // ************************************************************************** sos_Object _sos_Object_sos_Object_Mapping::__index (sos_Typed_id &_tpid,sos_Object key) // ************************************************************************** { T_PROC("Mapping::operator[]"); TT(agg_H,T_ENTER); sos_Object dummy_pred,dummy_succ,info; sos_Container ct = sos_Object_sos_Object_Mapping::make(_tpid,this).container(); sos_Bool key_based_on_equal = sos_Object_sos_Object_Mapping::make(_tpid,this).get_key_based_on_equal(); sos_Bool list_cursor = sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(); sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); sos_Int ht_entries = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(); sos_Offset ht_offset = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(); if (NOT get_entry(ct, list_cursor,key_based_on_equal,ht_entries, ht_offset,key,TRUE, info, dummy_pred,dummy_succ)) info = NO_OBJECT; TT(agg_H,T_LEAVE); return info; } // ** Mapping::operator[] ** // ************************************************************************** sos_Object _sos_Object_sos_Object_Mapping::get_key(sos_Typed_id &_tpid,sos_Cursor c) // ************************************************************************** { T_PROC("Mapping::get_key(sos_Cursor)"); TT(agg_H, T_ENTER); err_assert (c.defined_for (sos_Object_sos_Object_Mapping::make(_tpid,this)), "Mapping:get_key"); err_assert (sos_Object_sos_Object_Mapping::make(_tpid,this).is_valid(c), "Mapping:get_key"); sos_Map_node mn = sos_Map_node::make (c.get_current()); sos_Object o = mn.get_key_val(); TT(agg_H,T_LEAVE); return o; } // ** Mapping::get_key ** // ************************************************************************** sos_Object _sos_Object_sos_Object_Mapping::get_info(sos_Typed_id &_tpid,sos_Cursor c) // ************************************************************************** { T_PROC("Mapping::get_info(sos_Cursor)"); TT(agg_H, T_ENTER); err_assert (c.defined_for (sos_Object_sos_Object_Mapping::make(_tpid,this)), "Mapping:get_info"); err_assert(sos_Object_sos_Object_Mapping::make(_tpid,this).is_valid(c),"Mapping:get_info"); sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); sos_Object dummy_pred, dummy_succ; sos_Object key = sos_Map_node::make (c.get_current()).get_key_val(); sos_Object info; sos_Bool found = get_entry(sos_Object_sos_Object_Mapping::make(_tpid,this).container(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_key_based_on_equal(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(), key,TRUE, info,dummy_pred,dummy_succ); // Beliebter Benutzerfehler: Ein Prozess erzeugt // einen Eintrag mit einem Key im TEMP_CONTAINER, das // Programm beendet, ein anderer Prozess versucht darueber // zu itterieren. Trotz das im Key Bockmist steht, kann er // noch mit get_key geliefert werden. Wenn jedoch der passende Eintrag // dazu gesucht wird, wird der Hashwert vom Key gebraucht. In diesem // stehen wilde Sachen, der Eintrag wird (vermutlich) nicht gefunden. if (NOT found) err_raise(err_USE,"Entry didn't exists anymore","Mapping::get_info"); TT(agg_H, T_LEAVE); return info; } // ** Mapping::get_info ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::set_info(sos_Typed_id &_tpid,sos_Cursor c, sos_Object o) // ************************************************************************** { T_PROC ("Mapping::set_info"); TT (agg_H, T_ENTER); err_assert ((c.defined_for (sos_Object_sos_Object_Mapping::make(_tpid,this))), "Mapping:set_info"); sos_Object_sos_Object_Mapping::make(_tpid,this).insert (sos_Map_node::make (c.get_current()).get_key_val(), o); TT (agg_H, T_LEAVE); } // ** Mapping::set_info ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::move_cursor(sos_Typed_id &_tpid,sos_Cursor c, sos_Object key) // ************************************************************************** // sets the cursor c to the key object in the mapping that corresponds // to the given key { T_PROC ("Mapping::move_cursor"); TT( agg_H, T_ENTER); err_assert (c.defined_for (sos_Object_sos_Object_Mapping::make(_tpid,this)), "Mapping:move_cursor"); err_assert (sos_Object_sos_Object_Mapping::make(_tpid,this).is_key(key), "Mapping:move_cursor"); sos_Object dummy_pred, dummy_succ, dummy_info; sos_Bool found = get_entry(sos_Object_sos_Object_Mapping::make(_tpid,this).container(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_key_based_on_equal(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(),sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(), key,FALSE, dummy_info, dummy_pred,dummy_succ); err_assert (found, "Mapping:move_cursor"); sos_Map_node::make (c.get_current()).set_key_val(key); TT(agg_H, T_LEAVE); } // ** Mapping::move_cursor ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::insert_before (sos_Typed_id &_tpid,sos_Cursor c, sos_Object Key, sos_Object Entity) // ************************************************************************** { T_PROC("Mapping::insert_before"); TT(agg_H, T_ENTER); err_assert (sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(), "Mapping:insert_before"); err_assert (c.defined_for (sos_Object_sos_Object_Mapping::make(_tpid,this)), "Mapping:insert_before"); err_assert (sos_Object_sos_Object_Mapping::make(_tpid,this).is_valid(c), "Mapping:insert_before"); if (Key == sos_Object_sos_Object_Mapping::make(_tpid,this)) err_raise(err_USE,"Mapping itself cannot inserted","Mapping::insert"); sos_Map_node mn = sos_Map_node::make (c.get_current()); sos_Object dummy_info,pred,dummy_succ; sos_Object key = mn.get_key_val(); get_entry(sos_Object_sos_Object_Mapping::make(_tpid,this).container(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_key_based_on_equal(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(),sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(), key,FALSE, dummy_info, pred,dummy_succ); sos_Object_sos_Object_Mapping::make(_tpid,this).insert_in_list(Key, Entity, pred, mn.get_key_val()); TT(agg_H, T_LEAVE); } // ** Mapping::insert_before ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::insert_after (sos_Typed_id &_tpid,sos_Cursor c, sos_Object Key, sos_Object Entity) // ************************************************************************** { T_PROC("Mapping::insert_after"); TT(agg_H, T_ENTER); err_assert (sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(), "Mapping:insert_after"); err_assert (c.defined_for (sos_Object_sos_Object_Mapping::make(_tpid,this)), "Mapping:insert_after"); err_assert (sos_Object_sos_Object_Mapping::make(_tpid,this).is_valid(c), "Mapping:insert_after"); if (Key == sos_Object_sos_Object_Mapping::make(_tpid,this)) err_raise(err_USE,"Mapping itself cannot inserted","Mapping::insert"); sos_Map_node mn = sos_Map_node::make (c.get_current()); sos_Object dummy_info,dummy_pred,succ; sos_Object key = mn.get_key_val(); get_entry(sos_Object_sos_Object_Mapping::make(_tpid,this).container(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_key_based_on_equal(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(),sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(), key,FALSE,dummy_info,dummy_pred,succ); sos_Object_sos_Object_Mapping::make(_tpid,this).insert_in_list(Key, Entity, mn.get_key_val(), succ); TT(agg_H, T_LEAVE); } // ** Mapping::insert_after ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::local_assign (sos_Object_sos_Object_Mapping x, sos_Object o) // ************************************************************************** { T_PROC("Mapping::local_assign"); sos_Object_sos_Object_Mapping y = sos_Object_sos_Object_Mapping::make (o); sos_Container y_ct = y.container(); sos_Container x_ct = x.container(); sos_Bool x_list_cursor = x.get_list_cursor(); err_assert ((y.get_list_cursor() == x_list_cursor), "Mapping::local_assign"); destroy_ht(x_list_cursor, x_ct, x.get_ht_entries(), x.get_global_depth(), x.get_ht_offset()); sos_Bool key_based_on_equal = y.get_key_based_on_equal(); sos_Bool info_based_on_equal= y.get_info_based_on_equal(); sos_Int cardinality = y.get_cardinality(); sos_Int no_of_pages = y.get_no_of_pages(); sos_Int no_of_page_lists = y.get_no_of_page_lists(); sos_Int global_depth = y.get_global_depth(); sos_Int ht_entries = y.get_ht_entries(); sos_Int no_of_pages_offset = y.get_no_of_pages_offset(); x.set_key_based_on_equal(key_based_on_equal); x.set_info_based_on_equal(info_based_on_equal); x.set_cardinality(cardinality); x.set_no_of_pages(no_of_pages); x.set_no_of_page_lists(no_of_page_lists); x.set_global_depth(global_depth); x.set_ht_entries(ht_entries); x.set_first_object(y.get_first_object()); x.set_last_object(y.get_last_object()); if (cardinality != 0) { // kopiere das Feld fuer no_of_pages rueber sos_Offset new_no_of_pages_offset = x_ct.allocate(NO_OF_PAGES_ARRAY_SIZE); x.set_no_of_pages_offset(new_no_of_pages_offset); sos_Char mem [NO_OF_PAGES_ARRAY_SIZE]; y_ct.read(no_of_pages_offset, NO_OF_PAGES_ARRAY_SIZE, &mem); x_ct.write(new_no_of_pages_offset, NO_OF_PAGES_ARRAY_SIZE, &mem); // erzeuge die Hashtabelle x.set_ht_offset(x_ct.allocate(y.get_ht_entries() *HT_ENTRY_SIZE)); // Laufe die erste HT durch und kopiere die Seiten sos_Offset old_ht_offset = y.get_ht_offset(); sos_Offset new_ht_offset = x.get_ht_offset(); for (sos_Int ht_pos = 0;ht_pos<y.get_ht_entries();ht_pos++) { ht_entry_t ht_entry = read_ht_entry(y_ct,old_ht_offset,ht_pos); // Ist es der erste Zeiger auf die Seitenliste ? sos_Int local_depth = ht_entry.local_depth; sos_Int first_link = FIRST_LINK(ht_pos, local_depth); if (first_link == ht_pos) { // erster Verweis, kopiere die Seitenliste sos_Offset old_page_offset = ht_entry.page_list_offset; page_header_t old_first_page_header = read_page_header(y_ct, ht_entry.page_list_offset); sos_Offset pred_page_offset=0; for(sos_Int page_no=1;page_no<=old_first_page_header.pages;page_no++) { sos_Offset new_page_offset = x_ct.allocate(PAGE_SIZE(x_list_cursor)); if (page_no == 1) ht_entry.page_list_offset = new_page_offset; else { page_header_t tmp_page_header = old_first_page_header; tmp_page_header.next_page = new_page_offset; // schreibe den Seitenverweis in den Vorgaenger write_page_header(x_ct,pred_page_offset, tmp_page_header); } page_t tmp_page; sos_Int entries = (page_no < old_first_page_header.pages)? MAX_PAGE_ENTRIES(x_list_cursor): old_first_page_header.entries_on_last_page; // kopiere die Seite read_page(x_list_cursor,y_ct,old_page_offset,entries,tmp_page); write_page(x_list_cursor, x_ct,new_page_offset, MAX_PAGE_ENTRIES(x_list_cursor), tmp_page); pred_page_offset = new_page_offset; // lese den Seitenkopf der Seite, um // Nachfolgerseite rauszufinden page_header_t old_page_header = read_page_header(y_ct,old_page_offset); old_page_offset = old_page_header.next_page; } // kopiere alle Seiten // schreibe den HT-Eintrag in alle Verweise der HT sos_Int no_of_links = LSHIFT(1,global_depth-local_depth); for (sos_Int k=0;k<no_of_links;k++) { sos_Int x = LSHIFT(k,local_depth); sos_Int other_link = x BITOR ht_pos; write_ht_entry(x_ct,new_ht_offset,other_link,ht_entry); } // schreibe alle HT-Eintraege pro Seite } } if (x_list_cursor) { sos_Object my_no_object = x; x.write_succ_pred (x_ct, key_based_on_equal, x_list_cursor, x.get_first_object(), FALSE, my_no_object); x.write_succ_pred (x_ct, key_based_on_equal, x_list_cursor, x.get_last_object(), TRUE, my_no_object); } } else { // Kein Element drin, erzeuge nichts, initialisere wie in local_init sos_Object my_no_object = x; x.set_first_object(my_no_object); x.set_last_object(my_no_object); x.set_ht_offset(NIL); x.set_ht_entries(0); x.set_no_of_pages_offset(NIL); } TT(agg_H,T_LEAVE); } // ** Mapping::local_assign ** // ************************************************************************** sos_Bool _sos_Object_sos_Object_Mapping::local_equal (sos_Object_sos_Object_Mapping x, sos_Object o, sos_Eq_kind eq_kind) // ************************************************************************** { sos_Bool result; if ((eq_kind EQ EQ_STRONG AND NOT o.has_type(x.type())) OR (eq_kind EQ EQ_WEAK AND NOT o.isa (x.type()))) result = FALSE; else { sos_Object_sos_Object_Mapping y = sos_Object_sos_Object_Mapping::make (o); if (y.get_cardinality() != x.get_cardinality()) result = FALSE; else { sos_Bool info_based_on_equal = x.get_info_based_on_equal(); agg_iterate_association (x, sos_Object key, sos_Object info) { if (NOT agg_same_entity (y[key],info,info_based_on_equal,eq_kind)) { result = FALSE; break; } } agg_iterate_association_end (x, key, info); } } return result; } // ** Mapping::local_equal ** // ************************************************************************** sos_Int _sos_Object_sos_Object_Mapping::local_hash_value (sos_Object_sos_Object_Mapping m) // ************************************************************************** // Ansich muesste der augenblickliche Hashwert gespeichert werden, und bei // jedem eingefuegten Element mit einer reversiblen Operation verknuepft // werden. Bei jedem remove, muss diese reversible Operation aufgerufen // werden. Zu viel Aufwand, drum bisher noch nicht implementiert. { return m.card(); } // ** Mapping::local_hash_value ** // ************************************************************************** sos_Int _sos_Object_sos_Object_Mapping::size(sos_Typed_id &_tpid) // ************************************************************************** { sos_Int o_size = _sos_Object::size(_tpid); sos_Int ht_size = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries() * HT_ENTRY_SIZE; sos_Int nop_size = (ht_size>0)?NO_OF_PAGES_ARRAY_SIZE:0; sos_Int pg_size = sos_Object_sos_Object_Mapping::make(_tpid,this).get_no_of_pages()*PAGE_SIZE(sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor()); return o_size + sos_Object_sos_Object_Mapping::make(_tpid,this).container().rounded (ht_size) + sos_Object_sos_Object_Mapping::make(_tpid,this).container().rounded (pg_size) + sos_Object_sos_Object_Mapping::make(_tpid,this).container().rounded (nop_size); } // ** Mapping::size ** // ************************************************************************** sos_Bool _sos_Object_sos_Object_Mapping::is_role1 (sos_Typed_id &_tpid,sos_Object key) // ************************************************************************** { return sos_Object_sos_Object_Mapping::make(_tpid,this).is_key (key); } // ** Mapping::is_role1 ** // ************************************************************************** sos_Bool _sos_Object_sos_Object_Mapping::is_role2 (sos_Typed_id &_tpid,sos_Object info) // ************************************************************************** { return sos_Object_sos_Object_Mapping::make(_tpid,this).is_info (info); } // ** Mapping::is_role2 ** // ************************************************************************** sos_Object _sos_Object_sos_Object_Mapping::get_role1 (sos_Typed_id &_tpid,sos_Cursor c) // ************************************************************************** { return sos_Object_sos_Object_Mapping::make(_tpid,this).get_key (c); } // ** Mapping::get_role1 ** // ************************************************************************** sos_Object _sos_Object_sos_Object_Mapping::get_role2 (sos_Typed_id &_tpid,sos_Cursor c) // ************************************************************************** { return sos_Object_sos_Object_Mapping::make(_tpid,this).get_info (c); } // ** Mapping::get_role2 ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::remove_at(sos_Typed_id &_tpid,sos_Cursor c) // ************************************************************************** { T_PROC("Mapping::remove_at"); TT(agg_H, T_ENTER); err_assert (c.defined_for (sos_Object_sos_Object_Mapping::make(_tpid,this)), "Mapping:remove_at"); err_assert (sos_Object_sos_Object_Mapping::make(_tpid,this).is_valid(c), "Mapping:remove_at"); sos_Object key = sos_Object_sos_Object_Mapping::make(_tpid,this).get_key(c); if (sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor()) sos_Object_sos_Object_Mapping::make(_tpid,this).to_succ(c); else { // Setze Cursor auf my_no_object => is_valid == FALSE sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); sos_Map_node mn = sos_Map_node::make (c.get_current()); mn.set_key_val(my_no_object); } sos_Object_sos_Object_Mapping::make(_tpid,this).remove(key); TT(agg_H, T_LEAVE); } // ** Mapping::remove_at ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::clear(sos_Typed_id &_tpid) // ************************************************************************** { T_PROC("Mapping::clear"); TT(agg_H, T_ENTER); sos_Container ct = sos_Object_sos_Object_Mapping::make(_tpid,this).container(); sos_Int ht_entries = sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries(); if (ht_entries >0) destroy_ht(sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor(), ct, ht_entries, sos_Object_sos_Object_Mapping::make(_tpid,this).get_global_depth(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset()); sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); sos_Object_sos_Object_Mapping::make(_tpid,this).set_first_object(my_no_object); sos_Object_sos_Object_Mapping::make(_tpid,this).set_last_object(my_no_object); sos_Object_sos_Object_Mapping::make(_tpid,this).set_cardinality (0); sos_Object_sos_Object_Mapping::make(_tpid,this).set_ht_entries(0); sos_Object_sos_Object_Mapping::make(_tpid,this).set_global_depth(0); sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_pages_offset(NIL); sos_Object_sos_Object_Mapping::make(_tpid,this).set_no_of_pages(0); sos_Object_sos_Object_Mapping::make(_tpid,this).set_ht_offset(NIL); TT(agg_H, T_LEAVE); } // ** Mapping::clear ** // ************************************************************************** sos_Int _sos_Object_sos_Object_Mapping::card (sos_Typed_id &_tpid) // ************************************************************************** { T_PROC ("Mapping::card"); TT (agg_H, T_ENTER); sos_Int crd = sos_Object_sos_Object_Mapping::make(_tpid,this).get_cardinality(); TT (agg_H, T_LEAVE); return crd; } // ** Mapping::card ** // ************************************************************************** sos_Cursor _sos_Object_sos_Object_Mapping::open_cursor(sos_Typed_id &_tpid,sos_Container Cursor_ct) // ************************************************************************** { T_PROC ("Mapping::open_cursor"); TT( agg_H, T_ENTER); sos_Cursor c = sos_Cursor::create(Cursor_ct, sos_Object_sos_Object_Mapping::make(_tpid,this)); sos_Map_node mn = sos_Map_node::create(Cursor_ct); c.set_current(mn); sos_Object_sos_Object_Mapping::make(_tpid,this).to_first(c); TT(agg_H, T_LEAVE); return c; } // ** Mapping::open_cursor ** // ************************************************************************** void _sos_Object_sos_Object_Mapping::close_cursor(sos_Typed_id &_tpid,sos_Cursor c) // ************************************************************************** { err_assert ((c.defined_for (sos_Object_sos_Object_Mapping::make(_tpid,this))), "Mapping:close_cursor"); c.get_current().destroy(); c.destroy(); } // ** Mapping::close_cursor ** // ************************************************************************** sos_Cursor _sos_Object_sos_Object_Mapping::duplicate(sos_Typed_id &_tpid,sos_Cursor c) // ************************************************************************** { err_assert ((c.defined_for (sos_Object_sos_Object_Mapping::make(_tpid,this))), "Mapping:duplicate"); sos_Container Cursor_ct = c.container(); sos_Cursor dup_c = sos_Cursor::create(Cursor_ct, sos_Object_sos_Object_Mapping::make(_tpid,this)); sos_Map_node dup_mn = sos_Map_node::create(Cursor_ct); dup_c.set_current (dup_mn); dup_mn.set_key_val (sos_Map_node::make (c.get_current()).get_key_val()); return dup_c; } // ** Mapping::duplicate ** // ************************************************************************** sos_Bool _sos_Object_sos_Object_Mapping::is_valid (sos_Typed_id &_tpid,sos_Cursor c) // ************************************************************************** { sos_Map_node mn = sos_Map_node::make (c.get_current()); sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); sos_Bool valid = (mn.get_key_val() != my_no_object); return valid; } // ** is_valid ** // ************************************************************************** sos_Bool _sos_Object_sos_Object_Mapping::to_first(sos_Typed_id &_tpid,sos_Cursor c) // ************************************************************************** { err_assert ((c.defined_for (sos_Object_sos_Object_Mapping::make(_tpid,this))), "Mapping:to_first"); sos_Container Cursor_ct = c.container(); sos_Map_node current = sos_Map_node::make (c.get_current()); sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); sos_Object first; if (sos_Object_sos_Object_Mapping::make(_tpid,this).get_cardinality() > 0) { if (NOT (sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor())) first = read_first_object(sos_Object_sos_Object_Mapping::make(_tpid,this).container(),sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_cardinality(),my_no_object); else first = sos_Object_sos_Object_Mapping::make(_tpid,this).get_first_object(); } else first = my_no_object; current.set_key_val(first); return sos_Object_sos_Object_Mapping::make(_tpid,this).is_valid(c); } // ** Mapping::to_first ** // ************************************************************************** sos_Bool _sos_Object_sos_Object_Mapping::to_last(sos_Typed_id &_tpid,sos_Cursor c) // ************************************************************************** { err_assert ((c.defined_for (sos_Object_sos_Object_Mapping::make(_tpid,this))), "Mapping:to_last"); sos_Map_node current = sos_Map_node::make (c.get_current()); sos_Container Cursor_ct = c.container(); sos_Object my_no_object = sos_Object_sos_Object_Mapping::make(_tpid,this); sos_Object last; if (sos_Object_sos_Object_Mapping::make(_tpid,this).get_cardinality() > 0) { if (NOT (sos_Object_sos_Object_Mapping::make(_tpid,this).get_list_cursor())) last = read_last_object (sos_Object_sos_Object_Mapping::make(_tpid,this).container(), sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_offset(),sos_Object_sos_Object_Mapping::make(_tpid,this).get_cardinality(), my_no_object,sos_Object_sos_Object_Mapping::make(_tpid,this).get_ht_entries()); else last = sos_Object_sos_Object_Mapping::make(_tpid,this).get_last_object(); } else last = my_no_object; current.set_key_val(last); return sos_Object_sos_Object_Mapping::make(_tpid,this).is_valid(c); } // ** Mapping::to_last ** // ************************************************************************** sos_Bool _sos_Object_sos_Object_Mapping::to_succ(sos_Typed_id &_tpid,sos_Cursor c, Index i) // ************************************************************************** { err_assert (c.defined_for (sos_Object_sos_Object_Mapping::make(_tpid,this)), "Mapping:to_succ"); err_assert (sos_Object_sos_Object_Mapping::make(_tpid,this).is_valid(c), "Mapping:to_succ"); sos_Map_node mn = sos_Map_node::make (c.get_current()); sos_Object o = sos_Object_sos_Object_Mapping::make(_tpid,this).search_succ_pred (mn.get_key_val(),i); mn.set_key_val (o); return sos_Object_sos_Object_Mapping::make(_tpid,this).is_valid (c); } // ** Mapping::to_succ ** // ************************************************************************** sos_Bool _sos_Object_sos_Object_Mapping::to_pred(sos_Typed_id &_tpid,sos_Cursor c, Index i) // ************************************************************************** { return sos_Object_sos_Object_Mapping::make(_tpid,this).to_succ(c,-i); } // ** Mapping::to_pred **